Revision: 9794 https://osdn.net/projects/ttssh2/scm/svn/commits/9794 Author: zmatsuo Date: 2022-03-09 00:06:52 +0900 (Wed, 09 Mar 2022) Log Message: ----------- ビルド時コマンドラインの指定で複数種類のcygtermをビルドできるようにした - make - 出力 cygterm.exe - make cygterm+-x86_64 - 出力 cygterm+-x86_64/cygterm.exe - make cygterm+-i686 - 出力 cygterm+-i686/cygterm.exe Modified Paths: -------------- trunk/cygwin/cygterm/CMakeLists.txt trunk/cygwin/cygterm/Makefile trunk/cygwin/cygterm/build.bat trunk/cygwin/cygterm/build_cygterm.cmake Added Paths: ----------- trunk/cygwin/cygterm/cygterm.cpp trunk/cygwin/cygterm/cygterm_cfg.cpp Removed Paths: ------------- trunk/cygwin/cygterm/cygterm+-x86_64/cygterm.exe trunk/cygwin/cygterm/cygterm.cc trunk/cygwin/cygterm/cygterm_cfg.cc -------------- next part -------------- Modified: trunk/cygwin/cygterm/CMakeLists.txt =================================================================== --- trunk/cygwin/cygterm/CMakeLists.txt 2022-03-08 15:06:40 UTC (rev 9793) +++ trunk/cygwin/cygterm/CMakeLists.txt 2022-03-08 15:06:52 UTC (rev 9794) @@ -24,8 +24,8 @@ add_executable( ${PACKAGE_NAME} - cygterm.cc - cygterm_cfg.cc + cygterm.cpp + cygterm_cfg.cpp cygterm_cfg.h sub.cpp sub.h @@ -89,8 +89,8 @@ set(ARCHIVE "cygterm+.tar.gz") set(SRC - cygterm.cc - cygterm_cfg.cc + cygterm.cpp + cygterm_cfg.cpp cygterm_cfg.h sub.cpp sub.h Modified: trunk/cygwin/cygterm/Makefile =================================================================== --- trunk/cygwin/cygterm/Makefile 2022-03-08 15:06:40 UTC (rev 9793) +++ trunk/cygwin/cygterm/Makefile 2022-03-08 15:06:52 UTC (rev 9794) @@ -21,22 +21,38 @@ endif endif -CFLAGS = -D_GNU_SOURCE -O2 -fno-exceptions -DUNICODE -D_UNICODE +CFLAGS = -D_GNU_SOURCE -O2 -fno-exceptions -DUNICODE -D_UNICODE -MMD #CFLAGS = -D_GNU_SOURCE -O2 -fno-exceptions CXXFLAGS = $(CFLAGS) LDFLAGS = -mwindows -EXE = cygterm.exe +EXE_BASE = cygterm.exe SRC = \ - cygterm.cc \ - cygterm_cfg.cc \ + cygterm.cpp \ + cygterm_cfg.cpp \ + sub.cpp +H = \ cygterm_cfg.h \ - sub.cpp \ sub.h -CFG = cygterm.cfg + +#BINARY_DIR = cygterm_x86_64 + +ifeq (,$(BINARY_DIR)) +# output to current dir +EXE = $(EXE_BASE) +OBJ = $(SRC:.cpp=.o) RES = cygterm.res +else +# output to $(BINARY_DIR) +EXE = $(BINARY_DIR)/$(EXE_BASE) +OBJ = $(addprefix $(BINARY_DIR)/,$(SRC:.cpp=.o)) +RES = $(BINARY_DIR)/cygterm.res +endif +SRC_RC = cygterm.rc +DEP = $(OBJ:.o=.d) + ICO = cygterm.ico -SRC_RC = cygterm.rc +CFG = cygterm.cfg ARCHIVE = cygterm+.tar.gz .PHONY: all clean install uninstall cygterm_x86_64 cygterm_i686 @@ -43,20 +59,28 @@ all : $(EXE) +-include $(DEP) + archive : $(ARCHIVE) -$(EXE) : cygterm.o cygterm_cfg.o sub.o $(RES) - $(CC) $(CFLAGS) $(LDFLAGS) -o $(EXE) $^ -lole32 - strip $(EXE) +$(EXE) : $(OBJ) $(RES) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ -lole32 + strip $@ $(RES): $(SRC_RC) $(ICO) - $(RC) -O coff -o $(RES) $(SRC_RC) + @mkdir -p $(dir $@) + $(RC) -O coff -o $@ $(SRC_RC) $(SRC_RC): echo 'icon ICON $(ICO)' > $(SRC_RC) clean : - rm -f $(EXE) $(SRC_RC) $(RES) $(ARCHIVE) *.o *.obj + rm -f $(EXE) $(SRC_RC) $(RES) $(ARCHIVE) + rm -f $(DEP) + rm -f $(OBJ) *.o *.obj +ifneq (,$(BINARY_DIR)) + rm -rf $(BINARY_DIR) +endif install : $(EXE) @ install -v $(EXE) $(BINDIR)/$(EXE) @@ -68,25 +92,23 @@ rm -f $(BINDIR)/$(EXE) rm -f $(BINDIR)/$(CFG) -$(ARCHIVE) : $(SRC) $(ICO) $(CFG) README README-j Makefile CMakeLists.txt - tar cf - $(SRC) $(ICO) $(CFG) COPYING README README-j Makefile CMakeLists.txt msys2term.cfg | gzip > $(ARCHIVE) +$(ARCHIVE) : $(SRC) $(H) $(ICO) $(CFG) README README-j Makefile CMakeLists.txt + tar cf - $(SRC) $(H) $(ICO) $(CFG) COPYING README README-j Makefile CMakeLists.txt msys2term.cfg | gzip > $(ARCHIVE) cygterm.o: ifeq (0, $(shell nm /usr/lib/crt0.o | grep -c WinMainCRTStartup)) - $(CXX) $(CXXFLAGS) -DNO_WIN_MAIN cygterm.cc -c -o $@ + $(CXX) $(CXXFLAGS) -DNO_WIN_MAIN cygterm.cpp -c -o $@ else - $(CXX) $(CXXFLAGS) cygterm.cc -c -o $@ + $(CXX) $(CXXFLAGS) cygterm.cpp -c -o $@ endif -# cc -M *.cc *.cpp -cygterm.o: cygterm.cc sub.h cygterm_cfg.h -cygterm_cfg.o: cygterm_cfg.cc cygterm_cfg.h -sub.o: sub.cpp sub.h - # call sub make -cygterm_x86_64: - make CC=x86_64-pc-cygwin-gcc CXX=x86_64-pc-cygwin-g++ RC=x86_64-pc-cygwin-windres EXE=cygterm_x86_64.exe +cygterm+-x86_64: + make CC=x86_64-pc-cygwin-gcc CXX=x86_64-pc-cygwin-g++ RC=x86_64-pc-cygwin-windres EXE_BASE=cygterm.exe BINARY_DIR=cygterm+-x86_64 all -cygterm_i686: - make CC=i686-pc-cygwin-gcc CXX=i686-pc-cygwin-g++ RC=i686-pc-cygwin-windres clean all EXE=cygterm_i686.exe +cygterm+-i686: + make CC=i686-pc-cygwin-gcc CXX=i686-pc-cygwin-g++ RC=i686-pc-cygwin-windres EXE_BASE=cygterm.exe BINARY_DIR=cygterm+-i686 all +$(BINARY_DIR)/%.o: %.cpp + @mkdir -p $(BINARY_DIR) + $(CXX) $(CXXFLAGS) -c $< -o $@ Modified: trunk/cygwin/cygterm/build.bat =================================================================== --- trunk/cygwin/cygterm/build.bat 2022-03-08 15:06:40 UTC (rev 9793) +++ trunk/cygwin/cygterm/build.bat 2022-03-08 15:06:52 UTC (rev 9794) @@ -2,9 +2,8 @@ rem PATH=c:\cygwin\bin PATH=c:\cygwin64\bin uname -a > build_info.txt -make EXE=cygterm_i686.exe clean -make -j cygterm_i686 -make EXE=cygterm_x86_64.exe clean -make -j cygterm_x86_64 -file *.exe >> build_info.txt +make -j cygterm+-i686 +make -j cygterm+-x86_64 +file cygterm+-i686/*.exe >> build_info.txt +file cygterm+-x86_64/*.exe >> build_info.txt pause Modified: trunk/cygwin/cygterm/build_cygterm.cmake =================================================================== --- trunk/cygwin/cygterm/build_cygterm.cmake 2022-03-08 15:06:40 UTC (rev 9793) +++ trunk/cygwin/cygterm/build_cygterm.cmake 2022-03-08 15:06:52 UTC (rev 9794) @@ -1,6 +1,8 @@ # cygterm, msys2termのビルド # - 生成可能な cygterm, msys2term をビルド # - CMAKE_INSTALL_PREFIX にコピーする +# 例 +# mkdir build_all && cd build_all && cmake -P ../build_cygterm.cmake message("CMAKE_COMMAND=${CMAKE_COMMAND}") message("CMAKE_HOST_SYSTEM_NAME=${CMAKE_HOST_SYSTEM_NAME}") Deleted: trunk/cygwin/cygterm/cygterm+-x86_64/cygterm.exe =================================================================== (Binary files differ) Deleted: trunk/cygwin/cygterm/cygterm.cc =================================================================== --- trunk/cygwin/cygterm/cygterm.cc 2022-03-08 15:06:40 UTC (rev 9793) +++ trunk/cygwin/cygterm/cygterm.cc 2022-03-08 15:06:52 UTC (rev 9794) @@ -1,1434 +0,0 @@ -///////////////////////////////////////////////////////////////////////////// -// CygTerm+ - yet another Cygwin console -// Copyright (C) 2000-2006 NSym. -// (C) 2006- TeraTerm Project -//--------------------------------------------------------------------------- -// 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. -// -// Writtern by TeraTerm Project. -// https://ttssh2.osdn.jp/ -// -// Original written by NSym. -// - -#if !defined(__CYGWIN__) -#error check compiler -#endif - -// MessageBox\x82̃^\x83C\x83g\x83\x8B\x82Ŏg\x97p TODO exe\x83t\x83@\x83C\x83\x8B\x96\xBC\x82ɕύX -static char Program[] = "CygTerm+"; -//static char Version[] = "version 1.07_30_beta (2021/11/14)"; - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <ctype.h> -#include <fcntl.h> -#include <unistd.h> -#include <signal.h> -#include <errno.h> -#include <sys/types.h> -#include <sys/ioctl.h> -#include <termios.h> -#include <sys/time.h> -#include <sys/socket.h> -#include <sys/un.h> -#include <sys/wait.h> -#include <arpa/inet.h> -#include <windows.h> -#include <shlobj.h> -#include <pwd.h> -#include <sys/select.h> -#include <wchar.h> - -#include "sub.h" - -#include "cygterm_cfg.h" - -// pageant support (ssh-agent proxy) -//---------------------------------- -#define AGENT_COPYDATA_ID 0x804e50ba -#define AGENT_MAX_MSGLEN 8192 -char sockdir[] = "/tmp/ssh-XXXXXXXXXX"; -char sockname[256]; - -// PTY device name -//---------------- -#define DEVPTY "/dev/ptmx" - -// TCP port for TELNET -//-------------------- -#define PORT_START_DEFAULT 20000 // default lowest port number -#define PORT_RANGE_DEFAULT 40 // default number of ports - -// TCP port for connection to another terminal application -//-------------------------------------------------------- -int cl_port = 0; -u_short listen_port; - -// telnet socket timeout -//---------------------- -#define TELSOCK_TIMEOUT_DEFAULT 5 // timeout 5 sec - -// chdir to HOME -//-------------- -#define HOME_CHDIR_DEFAULT false - -// login shell flag -//----------------- -#define ENABLE_LOGINSHELL_DEFAULT false - -// ssh agent proxy -//---------------- -#define ENABLE_AGENT_PROXY_DEFAULT false - -// debug mode -//----------- -#define DEBUG_FLAG_DEFAULT false; -bool debug_flag = DEBUG_FLAG_DEFAULT; - -// "cygterm.cfg" -static char *cfg_base; // "cygterm.cfg" -static char *cfg_exe; // [exe directory]/cygterm.cfg -static char *conf_appdata_full; // $APPDATA/teraterm5/cygterm.cfg -static char *sys_conf; // /etc/cygterm.conf -static char *usr_conf; // ~/cygtermrc $HOME/cygtermrc - -//================// -// message output // -//----------------// -// msg \x82\xCD ANSI\x95\xB6\x8E\x9A\x83R\x81[\x83h (UTF8\x82͉\xBB\x82\xAF\x82\xE9) -void msg_print(const char* msg) -{ - OutputDebugStringA(msg); - MessageBoxA(NULL, msg, Program, MB_OK | MB_ICONINFORMATION | MB_TOPMOST); -} - -//=========================// -// Win32-API error message // -//-------------------------// -void api_error(const char* string = NULL) -{ - char msg[1024]; - char *ptr = msg; - if (string != NULL) - ptr += snprintf(ptr, sizeof(msg), "%s\n\n", string); - FormatMessageA( - FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - ptr, sizeof(msg)-(ptr-msg), NULL - ); - msg_print(msg); -} - -//=========================// -// C-runtime error message // -//-------------------------// -void c_error(const char* string = NULL) -{ - char msg[1024]; - char *ptr = msg; - if (string != NULL) - ptr += snprintf(ptr, sizeof(msg), "%s\n\n", string); - snprintf(ptr, sizeof(msg)-(ptr-msg), "%s\n", strerror(errno)); - msg_print(msg); -} - -//======================// -// debug message output // -//======================// -void debug_msg_print(const char* msg, ...) -{ - if (debug_flag) { - char *tmp1; - va_list arg; - va_start(arg, msg); - vasprintf(&tmp1, msg, arg); - va_end(arg); - - char *tmp2; - unsigned long pid = GetCurrentProcessId(); - asprintf(&tmp2, "dbg %lu: %s\n", pid, tmp1); - OutputDebugStringA(tmp2); - // printf("%s", tmp2); - free(tmp2); - free(tmp1); - } -} - -static void get_cfg_filenames() -{ - char *argv0 = GetModuleFileNameU8(); - // cfg base filename "cygterm.cfg" - char *p = strrchr(argv0, '.'); - *p = 0; // cut ".exe" - p = strrchr(argv0, '/') + 1; - cfg_base = (char *)malloc(strlen(p) + 5); - strcpy(cfg_base, p); - strcat(cfg_base, ".cfg"); - - // exe path - cfg_exe = (char *)malloc(strlen(argv0) + strlen(cfg_base)); - strcpy(cfg_exe, argv0); - p = strrchr(cfg_exe, '/') + 1; - strcpy(p, cfg_base); - free(argv0); - argv0 = NULL; - - // home $HOME/cygtermrc - const char *home = getenv("HOME"); - usr_conf = (char *)malloc(strlen(home) + strlen(cfg_base) + 2); - strcpy(usr_conf, home); - strcat(usr_conf, "/."); - strcat(usr_conf, cfg_base); - p = strrchr(usr_conf, '.'); // ".cfg" -> "rc" - strcpy(p, "rc"); - - // system - sys_conf = (char *)malloc(sizeof("/etc/") + strlen(cfg_base) + 2); - strcpy(sys_conf, "/etc/"); - strcat(sys_conf, cfg_base); - p = strrchr(sys_conf, '.'); - strcpy(p, ".conf"); // ".cfg" -> ".conf" - - // $APPDATA/teraterm5/cygterm.cfg - char *appdata = GetAppDataDirU8(); - const char *teraterm = "/teraterm5/"; - size_t len = strlen(appdata) + strlen(teraterm) + strlen(cfg_base) + 1; - conf_appdata_full = (char *)malloc(sizeof(char) * len); - strcpy(conf_appdata_full, appdata); - strcat(conf_appdata_full, teraterm); - strcat(conf_appdata_full, cfg_base); - free(appdata); -} - -/** - * read /etc/passwd - * get user name from getlogin(). if it fails, use $USERNAME instead. - * and get /etc/passwd information by getpwnam(3) with user name, - */ -static void get_username_and_shell(cfg_data_t *cfg) -{ - const char* username = getlogin(); - if (username == NULL) - username = getenv("USERNAME"); - if (username != NULL) { - struct passwd* pw_ent = getpwnam(username); - if (pw_ent != NULL) { - free(cfg->shell); - cfg->shell = strdup(pw_ent->pw_shell); - free(cfg->username); - cfg->username = strdup(pw_ent->pw_name); - } - else { - free(cfg->username); - cfg->username = strdup(username); - } - } -} - -#define _countof(_Array) (sizeof(_Array) / sizeof(_Array[0])) - -//====================// -// load configuration // -//--------------------// -static void load_cfg(cfg_data_t *cfg) -{ - // \x90ݒ\xE8\x83t\x83@\x83C\x83\x8B\x93ǂݍ\x9E\x82ݏ\x87 - // \x83\x8A\x83X\x83g\x82̏\xE3\x82̂ق\xA4\x82\xA9\x82\xE7\x90\xE6\x82ɓǂݍ\x9E\x82܂\xEA\x82\xE9 - // \x83\x8A\x83X\x83g\x82̉\xBA\x82̂ق\xA4\x82\xA9\x82\xE7\x8C\xE3\x82ɓǂݍ\x9E\x82܂\xEA\x82\xE9(\x82\xA0\x82Ə\x9F\x82\xBF) - // \x89\xBA\x82̕\xFB\x82\xAA\x97D\x90揇\x88ʂ\xAA\x8D\x82\x82\xA2 - // \x92ʏ펞 - char *conf_order_normal_list[] = { - cfg_exe, // [exe directory]/cygterm.cfg - sys_conf, // /etc/cygterm.conf - conf_appdata_full, // $APPDATA/teraterm5/cygterm.cfg - usr_conf // ~/cygtermrc - }; - const int conf_order_normal_count = (int)_countof(conf_order_normal_list); - - // \x83|\x81[\x83^\x83u\x83\x8B\x8E\x9E - char *conf_order_portable_list[] = { - cfg_exe, // [exe directory]/cygterm.cfg - }; - const int conf_order_portable_count = (int)_countof(conf_order_portable_list); - - char **conf_order_list; - int conf_order_count; - if (IsPortableMode()) { - // \x83|\x81[\x83^\x83u\x83\x8B\x8E\x9E - conf_order_list = conf_order_portable_list; - conf_order_count = conf_order_portable_count; - } - else { - // \x92ʏ펞 - conf_order_list = conf_order_normal_list; - conf_order_count = conf_order_normal_count; - } - - // \x8E\xC0\x8Dۂɓǂݍ\x9E\x82\xDE - for (int i = 0; i < conf_order_count; i++) { - const char *fname = conf_order_list[i]; - debug_msg_print("load %s", fname); - // ignore empty configuration file path - if (fname == NULL || strcmp(fname, "") == 0) { - debug_msg_print(" pass"); - continue; - } - - bool r = cfg->load(cfg, fname); - debug_msg_print(" %s", r ? "ok" : "ng"); - cfg->dump(cfg, debug_msg_print); - } -} - -void quote_cut(char *dst, size_t len, char *src) { - while (*src && len > 1) { - if (*src != '"') { - *dst++ = *src; - } - src++; - } - *dst = 0; -} - -//=======================// -// commandline arguments // -//-----------------------// -void get_args(char** argv, cfg_data_t *cfg) -{ - for (++argv; *argv != NULL; ++argv) { - if (!strcmp(*argv, "-t")) { // -t <terminal emulator> - if (*++argv == NULL) - break; - free(cfg->term); - cfg->term = strdup(*argv); - } - else if (!strcmp(*argv, "-p")) { // -p <port#> - if (*(argv+1) != NULL) { - ++argv; - cfg->cl_port = atoi(*argv); - } - } - else if (!strcmp(*argv, "-dumb")) { // -dumb - cfg->dumb = 1; - free(cfg->term_type); - cfg->term_type = strdup("dumb"); - } - else if (!strcmp(*argv, "-s")) { // -s <shell> - if (*++argv == NULL) - break; - if (strcasecmp(*argv, "AUTO") != 0) { - free(cfg->shell); - cfg->shell = strdup(*argv); - } - } - else if (!strcmp(*argv, "-cd")) { // -cd - cfg->home_chdir = true; - } - else if (!strcmp(*argv, "-nocd")) { // -nocd - cfg->home_chdir = false; - } - else if (!strcmp(*argv, "+cd")) { // +cd - cfg->home_chdir = false; - } - else if (!strcmp(*argv, "-ls")) { // -ls - cfg->enable_loginshell = true; - } - else if (!strcmp(*argv, "-nols")) { // -nols - cfg->enable_loginshell = false; - } - else if (!strcmp(*argv, "+ls")) { // +ls - cfg->enable_loginshell = false; - } - else if (!strcmp(*argv, "-A")) { // -A - cfg->enable_agent_proxy = true; - } - else if (!strcmp(*argv, "-a")) { // -a - cfg->enable_agent_proxy = false; - } - else if (!strcmp(*argv, "-v")) { // -v <additional env var> - if (*(argv+1) != NULL) { - sh_env_t *sh_env = cfg->sh_env; - ++argv; - sh_env->add1(sh_env, *argv); - } - } - else if (!strcmp(*argv, "-d")) { // -d <exec directory> - if (*++argv == NULL) - break; - char change_dir[256] = ""; - quote_cut(change_dir, sizeof(change_dir), *argv); - cfg->change_dir = strdup(change_dir); - } - else if (!strcmp(*argv, "-o")) { // -o <additional option for terminal> - if (*++argv == NULL) - break; - free(cfg->termopt); - cfg->termopt = strdup(*argv); - } - else if (!strcmp(*argv, "-debug")) { // -debug - cfg->debug_flag = true; - } - } -} - -//===================================// -// pageant support (ssh-agent proxy) // -//-----------------------------------// -unsigned long get_uint32(unsigned char *buff) -{ - return ((unsigned long)buff[0] << 24) + - ((unsigned long)buff[1] << 16) + - ((unsigned long)buff[2] << 8) + - ((unsigned long)buff[3]); -} - -void set_uint32(unsigned char *buff, unsigned long v) -{ - buff[0] = (unsigned char)(v >> 24); - buff[1] = (unsigned char)(v >> 16); - buff[2] = (unsigned char)(v >> 8); - buff[3] = (unsigned char)v; - return; -} - -unsigned long agent_request(unsigned char *out, unsigned long out_size, unsigned char *in) -{ - HWND hwnd; - char mapname[25]; - HANDLE fmap = NULL; - unsigned char *p = NULL; - COPYDATASTRUCT cds; - unsigned long len; - unsigned long ret = 0; - - if (out_size < 5) { - return 0; - } - if ((len = get_uint32(in)) > AGENT_MAX_MSGLEN) { - goto agent_error; - } - - hwnd = FindWindowA("Pageant", "Pageant"); - if (!hwnd) { - goto agent_error; - } - - sprintf(mapname, "PageantRequest%08x", (unsigned)GetCurrentThreadId()); - fmap = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, - 0, AGENT_MAX_MSGLEN, mapname); - if (!fmap) { - goto agent_error; - } - - if ((p = (unsigned char *)MapViewOfFile(fmap, FILE_MAP_WRITE, 0, 0, 0)) == NULL) { - goto agent_error; - } - - cds.dwData = AGENT_COPYDATA_ID; - cds.cbData = strlen(mapname) + 1; - cds.lpData = mapname; - - memcpy(p, in, len + 4); - if (SendMessageA(hwnd, WM_COPYDATA, (WPARAM)NULL, (LPARAM)&cds) > 0) { - len = get_uint32(p); - if (out_size >= len + 4) { - memcpy(out, p, len + 4); - ret = len + 4; - } - } - -agent_error: - if (p) { - UnmapViewOfFile(p); - } - if (fmap) { - CloseHandle(fmap); - } - if (ret == 0) { - set_uint32(out, 1); - out[4] = 5; // SSH_AGENT_FAILURE - } - - return ret; -} - -void sighandler(int sig) { - (void)sig; - unlink(sockname); - rmdir(sockdir); - exit(0); -}; - -struct connList { - int sock; - int recvlen; - int sendlen; - struct connList *next; - unsigned char ibuff[AGENT_MAX_MSGLEN]; - unsigned char obuff[AGENT_MAX_MSGLEN]; -}; - -int proc_recvd(struct connList *conn) -{ - int reqlen, len; - - if (conn->sendlen > 0) { - return 0; - } - - if (conn->recvlen < 4) { - return 0; - } - - reqlen = get_uint32(conn->ibuff) + 4; - if (conn->recvlen < reqlen) { - return 0; - } - - len = agent_request(conn->obuff, sizeof(conn->obuff), conn->ibuff); - - if (len > 0) { - conn->sendlen = len; - } - else { - set_uint32(conn->obuff, 1); - conn->obuff[4] = 5; // SSH_AGENT_FAILURE - conn->sendlen = 1; - } - - if (conn->recvlen == reqlen) { - conn->recvlen = 0; - } - else { - conn->recvlen -= reqlen; - memmove(conn->ibuff, conn->ibuff + reqlen, conn->recvlen); - } - - return 1; -} - -void agent_proxy() -{ - int sock, asock, ret; - long len; - struct sockaddr_un addr; - struct connList connections, *new_conn, *prev, *cur; - fd_set readfds, writefds, rfds, wfds; - struct sigaction act; - sigset_t blk; - - connections.next = NULL; - - if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { - c_error("agent_proxy: socket failed."); - exit(0); - } - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - strlcpy(addr.sun_path, sockname, sizeof(addr.sun_path)); - - if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - goto agent_thread_cleanup; - } - if (listen(sock, -1) < 0) { - goto agent_thread_cleanup; - } - - sigfillset(&blk); - sigdelset(&blk, SIGKILL); - sigdelset(&blk, SIGSTOP); - - memset(&act, 0, sizeof(act)); - act.sa_handler = sighandler; - act.sa_mask = blk; - sigaction(SIGINT, &act, NULL); - sigaction(SIGTERM, &act, NULL); - sigaction(SIGHUP, &act, NULL); - sigaction(SIGQUIT, &act, NULL); - - FD_ZERO(&readfds); - FD_ZERO(&writefds); - FD_SET(sock, &readfds); - - while (1) { - memcpy(&rfds, &readfds, sizeof(fd_set)); - memcpy(&wfds, &writefds, sizeof(fd_set)); - - select(FD_SETSIZE, &rfds, &wfds, NULL, NULL); - - if (FD_ISSET(sock, &rfds)) { - asock = accept(sock, NULL, NULL); - if (asock < 0) { - if (!(errno == EINTR || errno == ECONNABORTED)) { - break; - } - } - else { - new_conn = (struct connList *)malloc(sizeof(struct connList)); - if (new_conn == NULL) { - // no memory - close(sock); - } - else { - new_conn->sock = asock; - new_conn->recvlen = 0; - new_conn->sendlen = 0; - new_conn->next = connections.next; - connections.next = new_conn; - FD_SET(asock, &readfds); - } - } - } - - prev = &connections; - for (cur=connections.next; cur != NULL; cur = cur->next) { - if (FD_ISSET(cur->sock, &wfds)) { - if (cur->sendlen > 0) { - len = send(cur->sock, cur->obuff, cur->sendlen, 0); - if (len < 0) { - // write error - prev->next = cur->next; - shutdown(cur->sock, SHUT_RDWR); - close(cur->sock); - FD_CLR(cur->sock, &writefds); - FD_CLR(cur->sock, &readfds); - free(cur); - cur = prev; - continue; - } - else if (len >= cur->sendlen) { - cur->sendlen = 0; - - sigprocmask(SIG_BLOCK, &blk, NULL); - ret = proc_recvd(cur); - sigprocmask(SIG_UNBLOCK, &blk, NULL); - - if (ret) { - FD_SET(cur->sock, &writefds); - FD_CLR(cur->sock, &readfds); - } - else { - FD_CLR(cur->sock, &writefds); - FD_SET(cur->sock, &readfds); - } - } - else if (len > 0) { - cur->sendlen -= len; - memmove(cur->obuff, cur->obuff+len, cur->sendlen); - } - } - else { - FD_CLR(cur->sock, &writefds); - } - } - - if (FD_ISSET(cur->sock, &rfds)) { - len = recv(cur->sock, cur->ibuff + cur->recvlen, sizeof(cur->ibuff) - cur->recvlen, 0); - if (len > 0) { - cur->recvlen += len; - - sigprocmask(SIG_BLOCK, &blk, NULL); - ret = proc_recvd(cur); - sigprocmask(SIG_UNBLOCK, &blk, NULL); - - if (ret) { - FD_SET(cur->sock, &writefds); - FD_CLR(cur->sock, &readfds); - } - else { - FD_CLR(cur->sock, &writefds); - FD_SET(cur->sock, &readfds); - } - } - else if (len <= 0) { - // read error - prev->next = cur->next; - shutdown(cur->sock, SHUT_RDWR); - close(cur->sock); - FD_CLR(cur->sock, &readfds); - FD_CLR(cur->sock, &writefds); - free(cur); - cur = prev; - continue; - } - } - } - } - -agent_thread_cleanup: - shutdown(sock, SHUT_RDWR); - close(sock); - - unlink(sockname); - rmdir(sockdir); - - exit(0); -} - -static int exec_agent_proxy(sh_env_t *sh_env) -{ - int pid; - - if (mkdtemp(sockdir) == NULL) { - return -1; - } - snprintf(sockname, sizeof(sockname), "%s/agent.%ld", sockdir, (long)getpid()); - - if (sh_env->add(sh_env, "SSH_AUTH_SOCK", sockname)) { - return -1; - } - - if ((pid = fork()) < 0) { - return -1; - } - if (pid == 0) { - setsid(); - agent_proxy(); - } - return pid; -} - -//=============================// -// terminal emulator execution // -//-----------------------------// -DWORD WINAPI term_thread(LPVOID param) -{ - cfg_data_t *cfg = (cfg_data_t *)param; - - in_addr addr; - addr.s_addr = htonl(INADDR_LOOPBACK); - char *term; - asprintf(&term, cfg->term, inet_ntoa(addr), (int)ntohs(listen_port)); - if (cfg->termopt != NULL) { - char *tmp; - asprintf(&tmp, "%s %s", tmp, cfg->termopt); - free(term); - term = tmp; - } - - debug_msg_print("CreateProcess '%s'", term); - - STARTUPINFO si; - PROCESS_INFORMATION pi; - FillMemory(&si, sizeof(si), 0); - si.cb = sizeof(si); - si.dwFlags = STARTF_USESHOWWINDOW; - si.wShowWindow = SW_SHOW; - DWORD flag = 0; - -#if defined(UNICODE) - wchar_t *termT = ToWcharU8(term); -#else - char *termT = strdup(term); -#endif - - BOOL r = - CreateProcess( - NULL, termT, NULL, NULL, FALSE, flag, NULL, NULL, &si, &pi); - free(termT); - if (!r) { - api_error(term); - return 0; - } - WaitForSingleObject(pi.hProcess, INFINITE); - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - return 0; -} - -//=======================================// -// thread creation for terminal emulator // -//---------------------------------------// -HANDLE exec_term(cfg_data_t *cfg) -{ - DWORD id; - return CreateThread(NULL, 0, term_thread, cfg, 0, &id); -} - -//=======================================// -// listener socket for TELNET connection // -//---------------------------------------// -int listen_telnet(u_short* port, cfg_data_t *cfg) -{ - int lsock; - if ((lsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - return -1; - } - struct sockaddr_in addr; - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - int i; - for (i = 0; i < cfg->port_range; ++i) { // find an unused port# - addr.sin_port = htons(cfg->port_start + i); - if (bind(lsock, (struct sockaddr *)&addr, sizeof(addr)) == 0) { - break; - } - } - if (i == cfg->port_range) { - shutdown(lsock, 2); - close(lsock); - return -1; - } - if (listen(lsock, 1) != 0) { - shutdown(lsock, 2); - close(lsock); - return -1; - } - *port = addr.sin_port; - return lsock; -} - -//=============================// -// accept of TELNET connection // -//-----------------------------// -int accept_telnet(int lsock, cfg_data_t *cfg) -{ - fd_set rbits; - FD_ZERO(&rbits); - FD_SET(lsock, &rbits); - struct timeval tm; - tm.tv_sec = cfg->telsock_timeout; - tm.tv_usec = 0; - if (select(FD_SETSIZE, &rbits, 0, 0, &tm) <= 0) { - c_error("accept_telnet: select failed"); - return -1; - } - if (!FD_ISSET(lsock, &rbits)) { - c_error("accept_telnet: FD_ISSET failed"); - return -1; - } - int asock; - struct sockaddr_in addr; - int len = sizeof(addr); - if ((asock = accept(lsock, (struct sockaddr *)&addr, &len)) < 0) { - c_error("accept_telnet: accept failed"); - return -1; - } - if (getpeername(asock, (struct sockaddr *)&addr, &len) != 0) { - c_error("accept_telnet: getpeername failed"); - shutdown(asock, 2); - close(asock); - return -1; - } - if (addr.sin_addr.s_addr != htonl(INADDR_LOOPBACK)) { - // reject it except local connection - msg_print("not local connection"); - shutdown(asock, 2); - close(asock); - return -1; - } - return asock; -} - -//============================// -// connect to specified port# // -//----------------------------// -int connect_client() -{ - int csock; - if ((csock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - return -1; - } - struct sockaddr_in addr; - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - addr.sin_port = htons(cl_port); - if (connect(csock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - close(csock); - return -1; - } - return csock; -} - -//========================================// -// 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 // -//-----------------// -static int exec_shell(int* sh_pid, cfg_data_t *cfg) -{ - // open pty master - int master; - if ((master = open(DEVPTY, O_RDWR)) < 0) { - c_error("exec_shell: master pty open error"); - return -1; - } - int pid; - if ((pid = fork()) < 0) { - c_error("exec_shell: fork failed"); - return -1; - } - if (pid == 0) { - // detach from control tty - setsid(); - // open pty slave - int slave; - if ((slave = open(ptsname(master), O_RDWR)) < 0) { - c_error("exec_shell: slave pty open error"); - 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 - if (cfg->term_type != NULL) { - // set terminal type to $TERM - setenv("TERM", cfg->term_type, 1); - } - // set other additional env vars - int i = 0; - sh_env_t *sh_env = cfg->sh_env; - while(1) { - char *value; - const char *env = sh_env->get(sh_env, i++, &value); - if (env == NULL) { - break; - } - setenv(env, value, 1); - } - // change directory - if (cfg->change_dir != NULL) { - if (chdir(cfg->change_dir) < 0) { - char tmp[256]; - snprintf(tmp, 256, "exec_shell: Can't chdir to \"%s\".", cfg->change_dir); - tmp[255] = 0; - c_error(tmp); - } - } - else if (cfg->home_chdir) { - // chdir to home directory - const char *home_dir = getenv("HOME"); - // ignore chdir(2) system-call error. - chdir(home_dir); - } - // execute a shell - char *argv[32]; - char *cmd_shell = cfg->shell; - get_argv(argv, 32, cmd_shell); - cfg->shell = strdup(cmd_shell); - if (cfg->enable_loginshell) { - char shell_path[128]; - char *pos; - strcpy(shell_path, argv[0]); - if ((pos = strrchr(argv[0], '/')) != NULL) { - *pos = '-'; - argv[0] = pos; - } - debug_msg_print("execv '%s' (login shell)", shell_path); - execv(shell_path, argv); - } - else { - debug_msg_print("execv '%s'", argv[0]); - 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 // -//------------------// -class IOBuf -{ -private: - int fd; - u_char i_buf[4096]; - u_char o_buf[4096]; - int i_pos, i_len, o_pos; -public: - IOBuf(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 IOBuf::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 IOBuf::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 IOBuf::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 IOBuf::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 IOBuf::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; -} - -//=========================// -// 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 { oECHO=1, oSGA=3, oTERM=24, oNAWS=31 }; - -bool c_will_term = false; -bool c_will_naws = false; - -// terminal type & size -//--------------------- -char *term_type; -struct winsize win_size = {0,0,0,0}; - -// dumb terminal flag -//------------------- -bool dumb = false; - -u_char telnet_cmd(IOBuf* te) -{ - u_char cmd, c; - te->nextc(&cmd); - if (cmd == sSB) { - te->nextc(&c); - // accept terminal type request - if (c == oTERM) { // "SB TERM - te->nextc(&c); // IS - u_char* p = (u_char*)term_type; - te->nextc(p); // TERMINAL-TYPE - while (*p != nIAC) { - if (isupper(*p)) *p = _tolower(*p); - ++p; te->nextc(p); - } - *p = 0; - te->nextc(&c); // IAC SE" - return (u_char)oTERM; - } - // 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); - } - else if (cmd == nWILL || cmd == nWONT || cmd == nDO || cmd == nDONT) { - u_char c; - te->nextc(&c); - if (cmd == nWILL && c == oTERM) // "WILL TERM" - c_will_term = true; - else if (cmd == nWILL && c == oNAWS) // "WILL NAWS" - c_will_naws = true; - } - return cmd; -} - -//============================// -// TELNET initial negotiation // -//----------------------------// -void telnet_nego(int te_sock) -{ - IOBuf te = te_sock; - u_char c; - - // start terminal type negotiation - // IAC DO TERMINAL-TYPE - te.putc(nIAC); te.putc(nDO); te.putc(oTERM); - te.flush_out(); - te.nextc(&c); - if (c != nIAC) { - te.ungetc(); - return; - } - (void)telnet_cmd(&te); - if (c_will_term) { - // terminal type sub-negotiation - // IAC SB TERMINAL-TYPE SEND IAC SE - te.putc(nIAC); te.putc(sSB); te.putc(oTERM); - te.putc(sSEND); te.putc(nIAC); te.putc(sSE); - te.flush_out(); - // accept terminal type response - te.nextc(&c); - if (c != nIAC) { - te.ungetc(); - return; - } - (void)telnet_cmd(&te); - } - - // start terminal size negotiation - // IAC DO WINDOW-SIZE - te.putc(nIAC); te.putc(nDO); te.putc(oNAWS); - te.flush_out(); - te.nextc(&c); - if (c != nIAC) { - te.ungetc(); - return; - } - (void)telnet_cmd(&te); - if (c_will_naws) { - // accept terminal size response - te.nextc(&c); - if (c != nIAC) { - te.ungetc(); - return; - } - (void)telnet_cmd(&te); - } - - // SGA/ECHO - te.putc(nIAC); te.putc(nWILL); te.putc(oSGA); - te.putc(nIAC); te.putc(nDO); te.putc(oSGA); - te.putc(nIAC); te.putc(nWILL); te.putc(oECHO); - te.flush_out(); -} - -//=============================================// -// relaying of a terminal emulator and a shell // -//---------------------------------------------// -void telnet_session(int te_sock, int sh_pty) -{ - IOBuf te = te_sock; - IOBuf sh = sh_pty; - fd_set rtmp, rbits; - FD_ZERO(&rtmp); - FD_SET(te, &rtmp); - FD_SET(sh, &rtmp); - u_char c; - int cr = 0; - int cnt = 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) { - if (c == nIAC) { - // escape a TELNET IAC char - te.putc(c); - } - te.putc(c); - } - if (te.flush_out() == false) { - break; - } - if (cnt++ < 20) { - continue; // give priority to data from a shell - } - cnt = 0; - } - if (FD_ISSET(te, &rbits)) { - // send data from a terminal to a shell - if (te.flush_in() == false) { - break; - } - while (te.getc(&c) == true) { - if (c == nIAC && !dumb) { - 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 TELNET terminal emulator and Cygwin shell // -//---------------------------------------------------------// -int main(int argc, char** argv) -{ - (void)argc; - int listen_sock = -1; - int te_sock = -1; - int sh_pty = -1; - HANDLE hTerm = NULL; - int sh_pid, agent_pid = 0; - - - // configuration file (.cfg) path - get_cfg_filenames(); - debug_msg_print("cfg_base %s", cfg_base); - debug_msg_print("cfg_exe %s", cfg_exe); - debug_msg_print("conf_appdata_full %s", conf_appdata_full); - debug_msg_print("sys_conf %s", sys_conf); - debug_msg_print("usr_conf %s", usr_conf); - - - // set default values - cfg_data_t *cfg = create_cfg(); - cfg->port_start = PORT_START_DEFAULT; - cfg->port_range = PORT_RANGE_DEFAULT; - cfg->telsock_timeout = TELSOCK_TIMEOUT_DEFAULT; - cfg->home_chdir = HOME_CHDIR_DEFAULT; - cfg->enable_loginshell = ENABLE_LOGINSHELL_DEFAULT; - cfg->enable_agent_proxy = ENABLE_AGENT_PROXY_DEFAULT; - cfg->debug_flag = DEBUG_FLAG_DEFAULT; - - // load configuration - get_username_and_shell(cfg); // from /etc/passwd - cfg->dump(cfg, debug_msg_print); - load_cfg(cfg); - sh_env_t *sh_env = cfg->sh_env; - sh_env->add(sh_env, "SHELL", cfg->shell); - sh_env->add(sh_env, "USER", cfg->username); - debug_msg_print("loginshell %d", cfg->enable_loginshell); - cfg->dump(cfg, debug_msg_print); - - // read commandline arguments - get_args(argv, cfg); - cfg->dump(cfg, debug_msg_print); - - // restore values - debug_flag = cfg->debug_flag; - - if (cfg->shell == NULL) { - msg_print("missing shell"); - return 0; - } - if (cfg->term == NULL && cl_port <= 0) { - msg_print("missing terminal"); - return 0; - } - - if (cfg->change_dir != NULL) { - cfg->home_chdir = false; - if (cfg->enable_loginshell) { - sh_env->add(sh_env, "CHERE_INVOKING", "y"); - } - } - - // terminal side connection - if (cfg->cl_port > 0) { - // connect to the specified TCP port - cl_port = cfg->cl_port; - if ((te_sock = connect_client()) < 0) { - goto cleanup; - } - } else { - // prepare a TELNET listener socket - if ((listen_sock = listen_telnet(&listen_port, cfg)) < 0) { - goto cleanup; - } - debug_msg_print("execute terminal"); - - // execute a terminal emulator - if ((hTerm = exec_term(cfg)) == NULL) { - api_error("exec_term failed"); - goto cleanup; - } - // accept connection from the terminal emulator - if ((te_sock = accept_telnet(listen_sock, cfg)) < 0) { - goto cleanup; - } - shutdown(listen_sock, 2); - close(listen_sock); - listen_sock = -1; - } - // TELNET negotiation - term_type = cfg->term_type; - dumb = cfg->dumb; - if (!dumb) { - telnet_nego(te_sock); - } - - // execute ssh-agent proxy - if (cfg->enable_agent_proxy) { - agent_pid = exec_agent_proxy(sh_env); - } - - // execute a shell - debug_msg_print("execute shell"); - if ((sh_pty = exec_shell(&sh_pid, cfg)) < 0) { - debug_msg_print("exec_shell failed"); - goto cleanup; - } - // set initial pty window size - if (!dumb && c_will_naws && win_size.ws_col != 0) { - ioctl(sh_pty, TIOCSWINSZ, &win_size); - } - - debug_msg_print("entering telnet session"); - // relay the terminal emulator and the shell - telnet_session(te_sock, sh_pty); - - cleanup: - if (agent_pid > 0) { - kill(agent_pid, SIGTERM); - } - if (sh_pty >= 0) { - close(sh_pty); - kill(sh_pid, SIGKILL); - } - if (agent_pid > 0 || sh_pty >= 0) { - wait((int*)NULL); - } - if (listen_sock >= 0) { - shutdown(listen_sock, 2); - close(listen_sock); - } - if (te_sock >= 0) { - shutdown(te_sock, 2); - close(te_sock); - } - if (hTerm != NULL) { - WaitForSingleObject(hTerm, INFINITE); - CloseHandle(hTerm); - } - return 0; -} - -#ifdef NO_WIN_MAIN -// \x83\x8A\x83\x93\x83N\x8E\x9E\x82\xC9 -mwindows \x82\xF0\x8Ew\x92肵\x82Ă\xA2\x82\xE9\x82̂\xC5 -// \x8E\xC0\x8Ds\x83t\x83@\x83C\x83\x8B\x82\xCD subsystem=windows \x82Ő\xB6\x90\xAC\x82\xB3\x82\xEA\x82Ă\xA2\x82\xE9 -// \x83v\x83\x8D\x83O\x83\x89\x83\x80\x82̃G\x83\x93\x83g\x83\x8A\x82\xCD WinMainCRTStartup() \x82ƂȂ\xE9 -// cygwin\x82ŃC\x83\x93\x83X\x83g\x81[\x83\x8B\x82\xB3\x82\xEA\x82\xE9gcc 11.2 \x82ł͂Ƃ\xAD\x82Ɏw\x92肵\x82Ȃ\xAD\x82Ă\xE0 main() \x82\xAA\x83R\x81[\x83\x8B\x82\xB3\x82\xEA\x82\xE9 -// -// \x88ȑO\x82\xCCcygwin\x82\xCCgcc\x82ł͎\x9F\x82̃R\x81[\x83h\x82\xAA\x95K\x97v\x82\xBE\x82\xC1\x82\xBD\x82̂\xA9\x82\xE0\x82\xB5\x82\xEA\x82Ȃ\xA2 -// This program is an Win32 application but, start as Cygwin main(). -//------------------------------------------------------------------ -extern "C" { - void mainCRTStartup(void); - void WinMainCRTStartup(void) { mainCRTStartup(); } -}; -#endif - -//EOF Copied: trunk/cygwin/cygterm/cygterm.cpp (from rev 9793, trunk/cygwin/cygterm/cygterm.cc) =================================================================== --- trunk/cygwin/cygterm/cygterm.cpp (rev 0) +++ trunk/cygwin/cygterm/cygterm.cpp 2022-03-08 15:06:52 UTC (rev 9794) @@ -0,0 +1,1434 @@ +///////////////////////////////////////////////////////////////////////////// +// CygTerm+ - yet another Cygwin console +// Copyright (C) 2000-2006 NSym. +// (C) 2006- TeraTerm Project +//--------------------------------------------------------------------------- +// 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. +// +// Writtern by TeraTerm Project. +// https://ttssh2.osdn.jp/ +// +// Original written by NSym. +// + +#if !defined(__CYGWIN__) +#error check compiler +#endif + +// MessageBox\x82̃^\x83C\x83g\x83\x8B\x82Ŏg\x97p TODO exe\x83t\x83@\x83C\x83\x8B\x96\xBC\x82ɕύX +static char Program[] = "CygTerm+"; +//static char Version[] = "version 1.07_30_beta (2021/11/14)"; + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <fcntl.h> +#include <unistd.h> +#include <signal.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include <termios.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/wait.h> +#include <arpa/inet.h> +#include <windows.h> +#include <shlobj.h> +#include <pwd.h> +#include <sys/select.h> +#include <wchar.h> + +#include "sub.h" + +#include "cygterm_cfg.h" + +// pageant support (ssh-agent proxy) +//---------------------------------- +#define AGENT_COPYDATA_ID 0x804e50ba +#define AGENT_MAX_MSGLEN 8192 +char sockdir[] = "/tmp/ssh-XXXXXXXXXX"; +char sockname[256]; + +// PTY device name +//---------------- +#define DEVPTY "/dev/ptmx" + +// TCP port for TELNET +//-------------------- +#define PORT_START_DEFAULT 20000 // default lowest port number +#define PORT_RANGE_DEFAULT 40 // default number of ports + +// TCP port for connection to another terminal application +//-------------------------------------------------------- +int cl_port = 0; +u_short listen_port; + +// telnet socket timeout +//---------------------- +#define TELSOCK_TIMEOUT_DEFAULT 5 // timeout 5 sec + +// chdir to HOME +//-------------- +#define HOME_CHDIR_DEFAULT false + +// login shell flag +//----------------- +#define ENABLE_LOGINSHELL_DEFAULT false + +// ssh agent proxy +//---------------- +#define ENABLE_AGENT_PROXY_DEFAULT false + +// debug mode +//----------- +#define DEBUG_FLAG_DEFAULT false; +bool debug_flag = DEBUG_FLAG_DEFAULT; + +// "cygterm.cfg" +static char *cfg_base; // "cygterm.cfg" +static char *cfg_exe; // [exe directory]/cygterm.cfg +static char *conf_appdata_full; // $APPDATA/teraterm5/cygterm.cfg +static char *sys_conf; // /etc/cygterm.conf +static char *usr_conf; // ~/cygtermrc $HOME/cygtermrc + +//================// +// message output // +//----------------// +// msg \x82\xCD ANSI\x95\xB6\x8E\x9A\x83R\x81[\x83h (UTF8\x82͉\xBB\x82\xAF\x82\xE9) +void msg_print(const char* msg) +{ + OutputDebugStringA(msg); + MessageBoxA(NULL, msg, Program, MB_OK | MB_ICONINFORMATION | MB_TOPMOST); +} + +//=========================// +// Win32-API error message // +//-------------------------// +void api_error(const char* string = NULL) +{ + char msg[1024]; + char *ptr = msg; + if (string != NULL) + ptr += snprintf(ptr, sizeof(msg), "%s\n\n", string); + FormatMessageA( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + ptr, sizeof(msg)-(ptr-msg), NULL + ); + msg_print(msg); +} + +//=========================// +// C-runtime error message // +//-------------------------// +void c_error(const char* string = NULL) +{ + char msg[1024]; + char *ptr = msg; + if (string != NULL) + ptr += snprintf(ptr, sizeof(msg), "%s\n\n", string); + snprintf(ptr, sizeof(msg)-(ptr-msg), "%s\n", strerror(errno)); + msg_print(msg); +} + +//======================// +// debug message output // +//======================// +void debug_msg_print(const char* msg, ...) +{ + if (debug_flag) { + char *tmp1; + va_list arg; + va_start(arg, msg); + vasprintf(&tmp1, msg, arg); + va_end(arg); + + char *tmp2; + unsigned long pid = GetCurrentProcessId(); + asprintf(&tmp2, "dbg %lu: %s\n", pid, tmp1); + OutputDebugStringA(tmp2); + // printf("%s", tmp2); + free(tmp2); + free(tmp1); + } +} + +static void get_cfg_filenames() +{ + char *argv0 = GetModuleFileNameU8(); + // cfg base filename "cygterm.cfg" + char *p = strrchr(argv0, '.'); + *p = 0; // cut ".exe" + p = strrchr(argv0, '/') + 1; + cfg_base = (char *)malloc(strlen(p) + 5); + strcpy(cfg_base, p); + strcat(cfg_base, ".cfg"); + + // exe path + cfg_exe = (char *)malloc(strlen(argv0) + strlen(cfg_base)); + strcpy(cfg_exe, argv0); + p = strrchr(cfg_exe, '/') + 1; + strcpy(p, cfg_base); + free(argv0); + argv0 = NULL; + + // home $HOME/cygtermrc + const char *home = getenv("HOME"); + usr_conf = (char *)malloc(strlen(home) + strlen(cfg_base) + 2); + strcpy(usr_conf, home); + strcat(usr_conf, "/."); + strcat(usr_conf, cfg_base); + p = strrchr(usr_conf, '.'); // ".cfg" -> "rc" + strcpy(p, "rc"); + + // system + sys_conf = (char *)malloc(sizeof("/etc/") + strlen(cfg_base) + 2); + strcpy(sys_conf, "/etc/"); + strcat(sys_conf, cfg_base); + p = strrchr(sys_conf, '.'); + strcpy(p, ".conf"); // ".cfg" -> ".conf" + + // $APPDATA/teraterm5/cygterm.cfg + char *appdata = GetAppDataDirU8(); + const char *teraterm = "/teraterm5/"; + size_t len = strlen(appdata) + strlen(teraterm) + strlen(cfg_base) + 1; + conf_appdata_full = (char *)malloc(sizeof(char) * len); + strcpy(conf_appdata_full, appdata); + strcat(conf_appdata_full, teraterm); + strcat(conf_appdata_full, cfg_base); + free(appdata); +} + +/** + * read /etc/passwd + * get user name from getlogin(). if it fails, use $USERNAME instead. + * and get /etc/passwd information by getpwnam(3) with user name, + */ +static void get_username_and_shell(cfg_data_t *cfg) +{ + const char* username = getlogin(); + if (username == NULL) + username = getenv("USERNAME"); + if (username != NULL) { + struct passwd* pw_ent = getpwnam(username); + if (pw_ent != NULL) { + free(cfg->shell); + cfg->shell = strdup(pw_ent->pw_shell); + free(cfg->username); + cfg->username = strdup(pw_ent->pw_name); + } + else { + free(cfg->username); + cfg->username = strdup(username); + } + } +} + +#define _countof(_Array) (sizeof(_Array) / sizeof(_Array[0])) + +//====================// +// load configuration // +//--------------------// +static void load_cfg(cfg_data_t *cfg) +{ + // \x90ݒ\xE8\x83t\x83@\x83C\x83\x8B\x93ǂݍ\x9E\x82ݏ\x87 + // \x83\x8A\x83X\x83g\x82̏\xE3\x82̂ق\xA4\x82\xA9\x82\xE7\x90\xE6\x82ɓǂݍ\x9E\x82܂\xEA\x82\xE9 + // \x83\x8A\x83X\x83g\x82̉\xBA\x82̂ق\xA4\x82\xA9\x82\xE7\x8C\xE3\x82ɓǂݍ\x9E\x82܂\xEA\x82\xE9(\x82\xA0\x82Ə\x9F\x82\xBF) + // \x89\xBA\x82̕\xFB\x82\xAA\x97D\x90揇\x88ʂ\xAA\x8D\x82\x82\xA2 + // \x92ʏ펞 + char *conf_order_normal_list[] = { + cfg_exe, // [exe directory]/cygterm.cfg + sys_conf, // /etc/cygterm.conf + conf_appdata_full, // $APPDATA/teraterm5/cygterm.cfg + usr_conf // ~/cygtermrc + }; + const int conf_order_normal_count = (int)_countof(conf_order_normal_list); + + // \x83|\x81[\x83^\x83u\x83\x8B\x8E\x9E + char *conf_order_portable_list[] = { + cfg_exe, // [exe directory]/cygterm.cfg + }; + const int conf_order_portable_count = (int)_countof(conf_order_portable_list); + + char **conf_order_list; + int conf_order_count; + if (IsPortableMode()) { + // \x83|\x81[\x83^\x83u\x83\x8B\x8E\x9E + conf_order_list = conf_order_portable_list; + conf_order_count = conf_order_portable_count; + } + else { + // \x92ʏ펞 + conf_order_list = conf_order_normal_list; + conf_order_count = conf_order_normal_count; + } + + // \x8E\xC0\x8Dۂɓǂݍ\x9E\x82\xDE + for (int i = 0; i < conf_order_count; i++) { + const char *fname = conf_order_list[i]; + debug_msg_print("load %s", fname); + // ignore empty configuration file path + if (fname == NULL || strcmp(fname, "") == 0) { + debug_msg_print(" pass"); + continue; + } + + bool r = cfg->load(cfg, fname); + debug_msg_print(" %s", r ? "ok" : "ng"); + cfg->dump(cfg, debug_msg_print); + } +} + +void quote_cut(char *dst, size_t len, char *src) { + while (*src && len > 1) { + if (*src != '"') { + *dst++ = *src; + } + src++; + } + *dst = 0; +} + +//=======================// +// commandline arguments // +//-----------------------// +void get_args(char** argv, cfg_data_t *cfg) +{ + for (++argv; *argv != NULL; ++argv) { + if (!strcmp(*argv, "-t")) { // -t <terminal emulator> + if (*++argv == NULL) + break; + free(cfg->term); + cfg->term = strdup(*argv); + } + else if (!strcmp(*argv, "-p")) { // -p <port#> + if (*(argv+1) != NULL) { + ++argv; + cfg->cl_port = atoi(*argv); + } + } + else if (!strcmp(*argv, "-dumb")) { // -dumb + cfg->dumb = 1; + free(cfg->term_type); + cfg->term_type = strdup("dumb"); + } + else if (!strcmp(*argv, "-s")) { // -s <shell> + if (*++argv == NULL) + break; + if (strcasecmp(*argv, "AUTO") != 0) { + free(cfg->shell); + cfg->shell = strdup(*argv); + } + } + else if (!strcmp(*argv, "-cd")) { // -cd + cfg->home_chdir = true; + } + else if (!strcmp(*argv, "-nocd")) { // -nocd + cfg->home_chdir = false; + } + else if (!strcmp(*argv, "+cd")) { // +cd + cfg->home_chdir = false; + } + else if (!strcmp(*argv, "-ls")) { // -ls + cfg->enable_loginshell = true; + } + else if (!strcmp(*argv, "-nols")) { // -nols + cfg->enable_loginshell = false; + } + else if (!strcmp(*argv, "+ls")) { // +ls + cfg->enable_loginshell = false; + } + else if (!strcmp(*argv, "-A")) { // -A + cfg->enable_agent_proxy = true; + } + else if (!strcmp(*argv, "-a")) { // -a + cfg->enable_agent_proxy = false; + } + else if (!strcmp(*argv, "-v")) { // -v <additional env var> + if (*(argv+1) != NULL) { + sh_env_t *sh_env = cfg->sh_env; + ++argv; + sh_env->add1(sh_env, *argv); + } + } + else if (!strcmp(*argv, "-d")) { // -d <exec directory> + if (*++argv == NULL) + break; + char change_dir[256] = ""; + quote_cut(change_dir, sizeof(change_dir), *argv); + cfg->change_dir = strdup(change_dir); + } + else if (!strcmp(*argv, "-o")) { // -o <additional option for terminal> + if (*++argv == NULL) + break; + free(cfg->termopt); + cfg->termopt = strdup(*argv); + } + else if (!strcmp(*argv, "-debug")) { // -debug + cfg->debug_flag = true; + } + } +} + +//===================================// +// pageant support (ssh-agent proxy) // +//-----------------------------------// +unsigned long get_uint32(unsigned char *buff) +{ + return ((unsigned long)buff[0] << 24) + + ((unsigned long)buff[1] << 16) + + ((unsigned long)buff[2] << 8) + + ((unsigned long)buff[3]); +} + +void set_uint32(unsigned char *buff, unsigned long v) +{ + buff[0] = (unsigned char)(v >> 24); + buff[1] = (unsigned char)(v >> 16); + buff[2] = (unsigned char)(v >> 8); + buff[3] = (unsigned char)v; + return; +} + +unsigned long agent_request(unsigned char *out, unsigned long out_size, unsigned char *in) +{ + HWND hwnd; + char mapname[25]; + HANDLE fmap = NULL; + unsigned char *p = NULL; + COPYDATASTRUCT cds; + unsigned long len; + unsigned long ret = 0; + + if (out_size < 5) { + return 0; + } + if ((len = get_uint32(in)) > AGENT_MAX_MSGLEN) { + goto agent_error; + } + + hwnd = FindWindowA("Pageant", "Pageant"); + if (!hwnd) { + goto agent_error; + } + + sprintf(mapname, "PageantRequest%08x", (unsigned)GetCurrentThreadId()); + fmap = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, + 0, AGENT_MAX_MSGLEN, mapname); + if (!fmap) { + goto agent_error; + } + + if ((p = (unsigned char *)MapViewOfFile(fmap, FILE_MAP_WRITE, 0, 0, 0)) == NULL) { + goto agent_error; + } + + cds.dwData = AGENT_COPYDATA_ID; + cds.cbData = strlen(mapname) + 1; + cds.lpData = mapname; + + memcpy(p, in, len + 4); + if (SendMessageA(hwnd, WM_COPYDATA, (WPARAM)NULL, (LPARAM)&cds) > 0) { + len = get_uint32(p); + if (out_size >= len + 4) { + memcpy(out, p, len + 4); + ret = len + 4; + } + } + +agent_error: + if (p) { + UnmapViewOfFile(p); + } + if (fmap) { + CloseHandle(fmap); + } + if (ret == 0) { + set_uint32(out, 1); + out[4] = 5; // SSH_AGENT_FAILURE + } + + return ret; +} + +void sighandler(int sig) { + (void)sig; + unlink(sockname); + rmdir(sockdir); + exit(0); +}; + +struct connList { + int sock; + int recvlen; + int sendlen; + struct connList *next; + unsigned char ibuff[AGENT_MAX_MSGLEN]; + unsigned char obuff[AGENT_MAX_MSGLEN]; +}; + +int proc_recvd(struct connList *conn) +{ + int reqlen, len; + + if (conn->sendlen > 0) { + return 0; + } + + if (conn->recvlen < 4) { + return 0; + } + + reqlen = get_uint32(conn->ibuff) + 4; + if (conn->recvlen < reqlen) { + return 0; + } + + len = agent_request(conn->obuff, sizeof(conn->obuff), conn->ibuff); + + if (len > 0) { + conn->sendlen = len; + } + else { + set_uint32(conn->obuff, 1); + conn->obuff[4] = 5; // SSH_AGENT_FAILURE + conn->sendlen = 1; + } + + if (conn->recvlen == reqlen) { + conn->recvlen = 0; + } + else { + conn->recvlen -= reqlen; + memmove(conn->ibuff, conn->ibuff + reqlen, conn->recvlen); + } + + return 1; +} + +void agent_proxy() +{ + int sock, asock, ret; + long len; + struct sockaddr_un addr; + struct connList connections, *new_conn, *prev, *cur; + fd_set readfds, writefds, rfds, wfds; + struct sigaction act; + sigset_t blk; + + connections.next = NULL; + + if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { + c_error("agent_proxy: socket failed."); + exit(0); + } + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strlcpy(addr.sun_path, sockname, sizeof(addr.sun_path)); + + if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + goto agent_thread_cleanup; + } + if (listen(sock, -1) < 0) { + goto agent_thread_cleanup; + } + + sigfillset(&blk); + sigdelset(&blk, SIGKILL); + sigdelset(&blk, SIGSTOP); + + memset(&act, 0, sizeof(act)); + act.sa_handler = sighandler; + act.sa_mask = blk; + sigaction(SIGINT, &act, NULL); + sigaction(SIGTERM, &act, NULL); + sigaction(SIGHUP, &act, NULL); + sigaction(SIGQUIT, &act, NULL); + + FD_ZERO(&readfds); + FD_ZERO(&writefds); + FD_SET(sock, &readfds); + + while (1) { + memcpy(&rfds, &readfds, sizeof(fd_set)); + memcpy(&wfds, &writefds, sizeof(fd_set)); + + select(FD_SETSIZE, &rfds, &wfds, NULL, NULL); + + if (FD_ISSET(sock, &rfds)) { + asock = accept(sock, NULL, NULL); + if (asock < 0) { + if (!(errno == EINTR || errno == ECONNABORTED)) { + break; + } + } + else { + new_conn = (struct connList *)malloc(sizeof(struct connList)); + if (new_conn == NULL) { + // no memory + close(sock); + } + else { + new_conn->sock = asock; + new_conn->recvlen = 0; + new_conn->sendlen = 0; + new_conn->next = connections.next; + connections.next = new_conn; + FD_SET(asock, &readfds); + } + } + } + + prev = &connections; + for (cur=connections.next; cur != NULL; cur = cur->next) { + if (FD_ISSET(cur->sock, &wfds)) { + if (cur->sendlen > 0) { + len = send(cur->sock, cur->obuff, cur->sendlen, 0); + if (len < 0) { + // write error + prev->next = cur->next; + shutdown(cur->sock, SHUT_RDWR); + close(cur->sock); + FD_CLR(cur->sock, &writefds); + FD_CLR(cur->sock, &readfds); + free(cur); + cur = prev; + continue; + } + else if (len >= cur->sendlen) { + cur->sendlen = 0; + + sigprocmask(SIG_BLOCK, &blk, NULL); + ret = proc_recvd(cur); + sigprocmask(SIG_UNBLOCK, &blk, NULL); + + if (ret) { + FD_SET(cur->sock, &writefds); + FD_CLR(cur->sock, &readfds); + } + else { + FD_CLR(cur->sock, &writefds); + FD_SET(cur->sock, &readfds); + } + } + else if (len > 0) { + cur->sendlen -= len; + memmove(cur->obuff, cur->obuff+len, cur->sendlen); + } + } + else { + FD_CLR(cur->sock, &writefds); + } + } + + if (FD_ISSET(cur->sock, &rfds)) { + len = recv(cur->sock, cur->ibuff + cur->recvlen, sizeof(cur->ibuff) - cur->recvlen, 0); + if (len > 0) { + cur->recvlen += len; + + sigprocmask(SIG_BLOCK, &blk, NULL); + ret = proc_recvd(cur); + sigprocmask(SIG_UNBLOCK, &blk, NULL); + + if (ret) { + FD_SET(cur->sock, &writefds); + FD_CLR(cur->sock, &readfds); + } + else { + FD_CLR(cur->sock, &writefds); + FD_SET(cur->sock, &readfds); + } + } + else if (len <= 0) { + // read error + prev->next = cur->next; + shutdown(cur->sock, SHUT_RDWR); + close(cur->sock); + FD_CLR(cur->sock, &readfds); + FD_CLR(cur->sock, &writefds); + free(cur); + cur = prev; + continue; + } + } + } + } + +agent_thread_cleanup: + shutdown(sock, SHUT_RDWR); + close(sock); + + unlink(sockname); + rmdir(sockdir); + + exit(0); +} + +static int exec_agent_proxy(sh_env_t *sh_env) +{ + int pid; + + if (mkdtemp(sockdir) == NULL) { + return -1; + } + snprintf(sockname, sizeof(sockname), "%s/agent.%ld", sockdir, (long)getpid()); + + if (sh_env->add(sh_env, "SSH_AUTH_SOCK", sockname)) { + return -1; + } + + if ((pid = fork()) < 0) { + return -1; + } + if (pid == 0) { + setsid(); + agent_proxy(); + } + return pid; +} + +//=============================// +// terminal emulator execution // +//-----------------------------// +DWORD WINAPI term_thread(LPVOID param) +{ + cfg_data_t *cfg = (cfg_data_t *)param; + + in_addr addr; + addr.s_addr = htonl(INADDR_LOOPBACK); + char *term; + asprintf(&term, cfg->term, inet_ntoa(addr), (int)ntohs(listen_port)); + if (cfg->termopt != NULL) { + char *tmp; + asprintf(&tmp, "%s %s", tmp, cfg->termopt); + free(term); + term = tmp; + } + + debug_msg_print("CreateProcess '%s'", term); + + STARTUPINFO si; + PROCESS_INFORMATION pi; + FillMemory(&si, sizeof(si), 0); + si.cb = sizeof(si); + si.dwFlags = STARTF_USESHOWWINDOW; + si.wShowWindow = SW_SHOW; + DWORD flag = 0; + +#if defined(UNICODE) + wchar_t *termT = ToWcharU8(term); +#else + char *termT = strdup(term); +#endif + + BOOL r = + CreateProcess( + NULL, termT, NULL, NULL, FALSE, flag, NULL, NULL, &si, &pi); + free(termT); + if (!r) { + api_error(term); + return 0; + } + WaitForSingleObject(pi.hProcess, INFINITE); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + return 0; +} + +//=======================================// +// thread creation for terminal emulator // +//---------------------------------------// +HANDLE exec_term(cfg_data_t *cfg) +{ + DWORD id; + return CreateThread(NULL, 0, term_thread, cfg, 0, &id); +} + +//=======================================// +// listener socket for TELNET connection // +//---------------------------------------// +int listen_telnet(u_short* port, cfg_data_t *cfg) +{ + int lsock; + if ((lsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + return -1; + } + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + int i; + for (i = 0; i < cfg->port_range; ++i) { // find an unused port# + addr.sin_port = htons(cfg->port_start + i); + if (bind(lsock, (struct sockaddr *)&addr, sizeof(addr)) == 0) { + break; + } + } + if (i == cfg->port_range) { + shutdown(lsock, 2); + close(lsock); + return -1; + } + if (listen(lsock, 1) != 0) { + shutdown(lsock, 2); + close(lsock); + return -1; + } + *port = addr.sin_port; + return lsock; +} + +//=============================// +// accept of TELNET connection // +//-----------------------------// +int accept_telnet(int lsock, cfg_data_t *cfg) +{ + fd_set rbits; + FD_ZERO(&rbits); + FD_SET(lsock, &rbits); + struct timeval tm; + tm.tv_sec = cfg->telsock_timeout; + tm.tv_usec = 0; + if (select(FD_SETSIZE, &rbits, 0, 0, &tm) <= 0) { + c_error("accept_telnet: select failed"); + return -1; + } + if (!FD_ISSET(lsock, &rbits)) { + c_error("accept_telnet: FD_ISSET failed"); + return -1; + } + int asock; + struct sockaddr_in addr; + int len = sizeof(addr); + if ((asock = accept(lsock, (struct sockaddr *)&addr, &len)) < 0) { + c_error("accept_telnet: accept failed"); + return -1; + } + if (getpeername(asock, (struct sockaddr *)&addr, &len) != 0) { + c_error("accept_telnet: getpeername failed"); + shutdown(asock, 2); + close(asock); + return -1; + } + if (addr.sin_addr.s_addr != htonl(INADDR_LOOPBACK)) { + // reject it except local connection + msg_print("not local connection"); + shutdown(asock, 2); + close(asock); + return -1; + } + return asock; +} + +//============================// +// connect to specified port# // +//----------------------------// +int connect_client() +{ + int csock; + if ((csock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + return -1; + } + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + addr.sin_port = htons(cl_port); + if (connect(csock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + close(csock); + return -1; + } + return csock; +} + +//========================================// +// 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 // +//-----------------// +static int exec_shell(int* sh_pid, cfg_data_t *cfg) +{ + // open pty master + int master; + if ((master = open(DEVPTY, O_RDWR)) < 0) { + c_error("exec_shell: master pty open error"); + return -1; + } + int pid; + if ((pid = fork()) < 0) { + c_error("exec_shell: fork failed"); + return -1; + } + if (pid == 0) { + // detach from control tty + setsid(); + // open pty slave + int slave; + if ((slave = open(ptsname(master), O_RDWR)) < 0) { + c_error("exec_shell: slave pty open error"); + 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 + if (cfg->term_type != NULL) { + // set terminal type to $TERM + setenv("TERM", cfg->term_type, 1); + } + // set other additional env vars + int i = 0; + sh_env_t *sh_env = cfg->sh_env; + while(1) { + char *value; + const char *env = sh_env->get(sh_env, i++, &value); + if (env == NULL) { + break; + } + setenv(env, value, 1); + } + // change directory + if (cfg->change_dir != NULL) { + if (chdir(cfg->change_dir) < 0) { + char tmp[256]; + snprintf(tmp, 256, "exec_shell: Can't chdir to \"%s\".", cfg->change_dir); + tmp[255] = 0; + c_error(tmp); + } + } + else if (cfg->home_chdir) { + // chdir to home directory + const char *home_dir = getenv("HOME"); + // ignore chdir(2) system-call error. + chdir(home_dir); + } + // execute a shell + char *argv[32]; + char *cmd_shell = cfg->shell; + get_argv(argv, 32, cmd_shell); + cfg->shell = strdup(cmd_shell); + if (cfg->enable_loginshell) { + char shell_path[128]; + char *pos; + strcpy(shell_path, argv[0]); + if ((pos = strrchr(argv[0], '/')) != NULL) { + *pos = '-'; + argv[0] = pos; + } + debug_msg_print("execv '%s' (login shell)", shell_path); + execv(shell_path, argv); + } + else { + debug_msg_print("execv '%s'", argv[0]); + 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 // +//------------------// +class IOBuf +{ +private: + int fd; + u_char i_buf[4096]; + u_char o_buf[4096]; + int i_pos, i_len, o_pos; +public: + IOBuf(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 IOBuf::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 IOBuf::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 IOBuf::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 IOBuf::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 IOBuf::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; +} + +//=========================// +// 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 { oECHO=1, oSGA=3, oTERM=24, oNAWS=31 }; + +bool c_will_term = false; +bool c_will_naws = false; + +// terminal type & size +//--------------------- +char *term_type; +struct winsize win_size = {0,0,0,0}; + +// dumb terminal flag +//------------------- +bool dumb = false; + +u_char telnet_cmd(IOBuf* te) +{ + u_char cmd, c; + te->nextc(&cmd); + if (cmd == sSB) { + te->nextc(&c); + // accept terminal type request + if (c == oTERM) { // "SB TERM + te->nextc(&c); // IS + u_char* p = (u_char*)term_type; + te->nextc(p); // TERMINAL-TYPE + while (*p != nIAC) { + if (isupper(*p)) *p = _tolower(*p); + ++p; te->nextc(p); + } + *p = 0; + te->nextc(&c); // IAC SE" + return (u_char)oTERM; + } + // 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); + } + else if (cmd == nWILL || cmd == nWONT || cmd == nDO || cmd == nDONT) { + u_char c; + te->nextc(&c); + if (cmd == nWILL && c == oTERM) // "WILL TERM" + c_will_term = true; + else if (cmd == nWILL && c == oNAWS) // "WILL NAWS" + c_will_naws = true; + } + return cmd; +} + +//============================// +// TELNET initial negotiation // +//----------------------------// +void telnet_nego(int te_sock) +{ + IOBuf te = te_sock; + u_char c; + + // start terminal type negotiation + // IAC DO TERMINAL-TYPE + te.putc(nIAC); te.putc(nDO); te.putc(oTERM); + te.flush_out(); + te.nextc(&c); + if (c != nIAC) { + te.ungetc(); + return; + } + (void)telnet_cmd(&te); + if (c_will_term) { + // terminal type sub-negotiation + // IAC SB TERMINAL-TYPE SEND IAC SE + te.putc(nIAC); te.putc(sSB); te.putc(oTERM); + te.putc(sSEND); te.putc(nIAC); te.putc(sSE); + te.flush_out(); + // accept terminal type response + te.nextc(&c); + if (c != nIAC) { + te.ungetc(); + return; + } + (void)telnet_cmd(&te); + } + + // start terminal size negotiation + // IAC DO WINDOW-SIZE + te.putc(nIAC); te.putc(nDO); te.putc(oNAWS); + te.flush_out(); + te.nextc(&c); + if (c != nIAC) { + te.ungetc(); + return; + } + (void)telnet_cmd(&te); + if (c_will_naws) { + // accept terminal size response + te.nextc(&c); + if (c != nIAC) { + te.ungetc(); + return; + } + (void)telnet_cmd(&te); + } + + // SGA/ECHO + te.putc(nIAC); te.putc(nWILL); te.putc(oSGA); + te.putc(nIAC); te.putc(nDO); te.putc(oSGA); + te.putc(nIAC); te.putc(nWILL); te.putc(oECHO); + te.flush_out(); +} + +//=============================================// +// relaying of a terminal emulator and a shell // +//---------------------------------------------// +void telnet_session(int te_sock, int sh_pty) +{ + IOBuf te = te_sock; + IOBuf sh = sh_pty; + fd_set rtmp, rbits; + FD_ZERO(&rtmp); + FD_SET(te, &rtmp); + FD_SET(sh, &rtmp); + u_char c; + int cr = 0; + int cnt = 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) { + if (c == nIAC) { + // escape a TELNET IAC char + te.putc(c); + } + te.putc(c); + } + if (te.flush_out() == false) { + break; + } + if (cnt++ < 20) { + continue; // give priority to data from a shell + } + cnt = 0; + } + if (FD_ISSET(te, &rbits)) { + // send data from a terminal to a shell + if (te.flush_in() == false) { + break; + } + while (te.getc(&c) == true) { + if (c == nIAC && !dumb) { + 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 TELNET terminal emulator and Cygwin shell // +//---------------------------------------------------------// +int main(int argc, char** argv) +{ + (void)argc; + int listen_sock = -1; + int te_sock = -1; + int sh_pty = -1; + HANDLE hTerm = NULL; + int sh_pid, agent_pid = 0; + + + // configuration file (.cfg) path + get_cfg_filenames(); + debug_msg_print("cfg_base %s", cfg_base); + debug_msg_print("cfg_exe %s", cfg_exe); + debug_msg_print("conf_appdata_full %s", conf_appdata_full); + debug_msg_print("sys_conf %s", sys_conf); + debug_msg_print("usr_conf %s", usr_conf); + + + // set default values + cfg_data_t *cfg = create_cfg(); + cfg->port_start = PORT_START_DEFAULT; + cfg->port_range = PORT_RANGE_DEFAULT; + cfg->telsock_timeout = TELSOCK_TIMEOUT_DEFAULT; + cfg->home_chdir = HOME_CHDIR_DEFAULT; + cfg->enable_loginshell = ENABLE_LOGINSHELL_DEFAULT; + cfg->enable_agent_proxy = ENABLE_AGENT_PROXY_DEFAULT; + cfg->debug_flag = DEBUG_FLAG_DEFAULT; + + // load configuration + get_username_and_shell(cfg); // from /etc/passwd + cfg->dump(cfg, debug_msg_print); + load_cfg(cfg); + sh_env_t *sh_env = cfg->sh_env; + sh_env->add(sh_env, "SHELL", cfg->shell); + sh_env->add(sh_env, "USER", cfg->username); + debug_msg_print("loginshell %d", cfg->enable_loginshell); + cfg->dump(cfg, debug_msg_print); + + // read commandline arguments + get_args(argv, cfg); + cfg->dump(cfg, debug_msg_print); + + // restore values + debug_flag = cfg->debug_flag; + + if (cfg->shell == NULL) { + msg_print("missing shell"); + return 0; + } + if (cfg->term == NULL && cl_port <= 0) { + msg_print("missing terminal"); + return 0; + } + + if (cfg->change_dir != NULL) { + cfg->home_chdir = false; + if (cfg->enable_loginshell) { + sh_env->add(sh_env, "CHERE_INVOKING", "y"); + } + } + + // terminal side connection + if (cfg->cl_port > 0) { + // connect to the specified TCP port + cl_port = cfg->cl_port; + if ((te_sock = connect_client()) < 0) { + goto cleanup; + } + } else { + // prepare a TELNET listener socket + if ((listen_sock = listen_telnet(&listen_port, cfg)) < 0) { + goto cleanup; + } + debug_msg_print("execute terminal"); + + // execute a terminal emulator + if ((hTerm = exec_term(cfg)) == NULL) { + api_error("exec_term failed"); + goto cleanup; + } + // accept connection from the terminal emulator + if ((te_sock = accept_telnet(listen_sock, cfg)) < 0) { + goto cleanup; + } + shutdown(listen_sock, 2); + close(listen_sock); + listen_sock = -1; + } + // TELNET negotiation + term_type = cfg->term_type; + dumb = cfg->dumb; + if (!dumb) { + telnet_nego(te_sock); + } + + // execute ssh-agent proxy + if (cfg->enable_agent_proxy) { + agent_pid = exec_agent_proxy(sh_env); + } + + // execute a shell + debug_msg_print("execute shell"); + if ((sh_pty = exec_shell(&sh_pid, cfg)) < 0) { + debug_msg_print("exec_shell failed"); + goto cleanup; + } + // set initial pty window size + if (!dumb && c_will_naws && win_size.ws_col != 0) { + ioctl(sh_pty, TIOCSWINSZ, &win_size); + } + + debug_msg_print("entering telnet session"); + // relay the terminal emulator and the shell + telnet_session(te_sock, sh_pty); + + cleanup: + if (agent_pid > 0) { + kill(agent_pid, SIGTERM); + } + if (sh_pty >= 0) { + close(sh_pty); + kill(sh_pid, SIGKILL); + } + if (agent_pid > 0 || sh_pty >= 0) { + wait((int*)NULL); + } + if (listen_sock >= 0) { + shutdown(listen_sock, 2); + close(listen_sock); + } + if (te_sock >= 0) { + shutdown(te_sock, 2); + close(te_sock); + } + if (hTerm != NULL) { + WaitForSingleObject(hTerm, INFINITE); + CloseHandle(hTerm); + } + return 0; +} + +#ifdef NO_WIN_MAIN +// \x83\x8A\x83\x93\x83N\x8E\x9E\x82\xC9 -mwindows \x82\xF0\x8Ew\x92肵\x82Ă\xA2\x82\xE9\x82̂\xC5 +// \x8E\xC0\x8Ds\x83t\x83@\x83C\x83\x8B\x82\xCD subsystem=windows \x82Ő\xB6\x90\xAC\x82\xB3\x82\xEA\x82Ă\xA2\x82\xE9 +// \x83v\x83\x8D\x83O\x83\x89\x83\x80\x82̃G\x83\x93\x83g\x83\x8A\x82\xCD WinMainCRTStartup() \x82ƂȂ\xE9 +// cygwin\x82ŃC\x83\x93\x83X\x83g\x81[\x83\x8B\x82\xB3\x82\xEA\x82\xE9gcc 11.2 \x82ł͂Ƃ\xAD\x82Ɏw\x92肵\x82Ȃ\xAD\x82Ă\xE0 main() \x82\xAA\x83R\x81[\x83\x8B\x82\xB3\x82\xEA\x82\xE9 +// +// \x88ȑO\x82\xCCcygwin\x82\xCCgcc\x82ł͎\x9F\x82̃R\x81[\x83h\x82\xAA\x95K\x97v\x82\xBE\x82\xC1\x82\xBD\x82̂\xA9\x82\xE0\x82\xB5\x82\xEA\x82Ȃ\xA2 +// This program is an Win32 application but, start as Cygwin main(). +//------------------------------------------------------------------ +extern "C" { + void mainCRTStartup(void); + void WinMainCRTStartup(void) { mainCRTStartup(); } +}; +#endif + +//EOF Deleted: trunk/cygwin/cygterm/cygterm_cfg.cc =================================================================== --- trunk/cygwin/cygterm/cygterm_cfg.cc 2022-03-08 15:06:40 UTC (rev 9793) +++ trunk/cygwin/cygterm/cygterm_cfg.cc 2022-03-08 15:06:52 UTC (rev 9794) @@ -1,406 +0,0 @@ -/* - * (C) 2022- TeraTerm Project - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <stdio.h> -#include <string.h> -#include <ctype.h> -#include <stdlib.h> - -#include "cygterm_cfg.h" - -#define DUMP_ENABLE 1 - -// additional env vars given to a shell -//------------------------------------- - -typedef struct sh_env_data_t { - struct sh_env_data_t* next; - char *name; - char *value; -} sh_env_data_t; - -typedef struct sh_env_private_tag { - sh_env_data_t *envp; -} sh_env_private_t; - -static bool env_add(sh_env_t *envp, const char* name, const char* value) -{ - sh_env_private_t *pr_data = (sh_env_private_t *)envp->private_data; - sh_env_data_t* e; - - if (name[0] == 0) { - return true; - } - e = (sh_env_data_t*)malloc(sizeof(*e)); - if (e == NULL) { - return false; - } - - e->name = strdup(name); - e->value = strdup(value); - e->next = NULL; - - if (pr_data->envp == NULL) { - pr_data->envp = e; - return true; - } - sh_env_data_t* env_data = pr_data->envp; - sh_env_data_t* prev_env = NULL; - while(1) { - if (strcmp(env_data->name, name) == 0) { - // \x93\xAF\x82\xB6\x96\xBC\x91O -> \x93\xFC\x82\xEA\x91ւ\xA6 - if (prev_env == NULL) { - pr_data->envp = e; - } else { - prev_env->next = e; - e->next = env_data->next; - } - free(env_data->name); - free(env_data->value); - free(env_data); - break; - } - if (env_data->next == NULL) { - // \x8DŌ\xE3\x82܂ŗ\x88\x82\xBD - env_data->next = e; - break; - } - prev_env = env_data; - env_data = env_data->next; - } - - return true; -} - -static bool env_add1(sh_env_t *envp, const char* name_value) -{ - if (name_value[0] == 0) { - return true; - } - char *name = strdup(name_value); - char *p = strchr(name, '='); - char *value; - if (p == NULL) { - value = NULL; - } - else { - *p = 0; - value = strdup(p+1); - } - bool r = env_add(envp, name, value); - free(value); - free(name); - return r; -} - -static char *env_get(sh_env_t *envp, int index, char **value) -{ - sh_env_private_t *pr_data = (sh_env_private_t *)envp->private_data; - sh_env_data_t* e = pr_data->envp; - if (e == NULL) { - return NULL; - } - while(1) { - if (index == 0) { - *value = e->value; - return e->name;; - } - if (e->next == NULL) { - *value = NULL; - return NULL; - } - index--; - e = e->next; - } -} - -static void env_destry_all(sh_env_t *envp) -{ - sh_env_private_t *pr_data = (sh_env_private_t *)envp->private_data; - sh_env_data_t* e = pr_data->envp; - if (e == NULL) { - return; - } - pr_data->envp = NULL; - while(1) { - sh_env_data_t* e_next = e->next; - e->next = NULL; - free(e); - e = e_next; - if (e == NULL) { - break; - } - } -} - -static void env_destry(sh_env_t *envp) -{ - sh_env_private_t *pr_data = (sh_env_private_t *)envp->private_data; - env_destry_all(envp); - free(pr_data); - envp->private_data = NULL; - free(envp); -} - -sh_env_t *create_sh_env(void) -{ - sh_env_t *sh_env = (sh_env_t *)calloc(sizeof(*sh_env), 1); - if (sh_env == NULL) { - return NULL; - } - sh_env_private_t *pr_data = (sh_env_private_t *)calloc(sizeof(*pr_data), 1); - if (pr_data == NULL) { - free(sh_env); - return NULL; - } - sh_env->private_data = pr_data; - sh_env->destroy = env_destry; - sh_env->add = env_add; - sh_env->add1 = env_add1; - sh_env->get = env_get; - - return sh_env; -} - -static bool is_bool_string(const char *s) -{ - if (strchr("YyTt", *s) != NULL) - return true; - if (atoi(s) > 0) - return true; - - return false; -} - -//==================================// -// parse line in configuration file // -//----------------------------------// -static void parse_cfg_line(char *buf, cfg_data_t *cfg) -{ - // "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)) { - return; // 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 != '=') { - return; // igonore line without '=' - } - for (++p2; isspace(*p2); ++p2); - char* val = p2; - for (p2 += strlen(p2); isspace(*(p2-1)); --p2); - *p1 = *p2 = 0; - - if (!strcasecmp(name, "TERM")) { - // terminal emulator command line (host:%s, port#:%d) - free(cfg->term); - cfg->term = strdup(val); - } - else if (!strcasecmp(name, "SHELL")) { - // shell command line - if (strcasecmp(val, "AUTO") != 0) { - free(cfg->shell); - cfg->shell = strdup(val); - } - } - else if (!strcasecmp(name, "PORT_START")) { - // minimum port# for TELNET - cfg->port_start = atoi(val); - } - else if (!strcasecmp(name, "PORT_RANGE")) { - // number of ports for TELNET - cfg->port_range = atoi(val); - } - else if (!strcasecmp(name, "TERM_TYPE")) { - // terminal type name (maybe overridden by TELNET negotiation.) - free(cfg->term_type); - cfg->term_type = strdup(val); - } - else if (!strncasecmp(name, "ENV_", 4)) { - // additional env vars given to a shell - sh_env_t *sh_env = cfg->sh_env; - sh_env->add1(sh_env, val); - } - else if (!strcasecmp(name, "HOME_CHDIR")) { - // change directory to home - if (is_bool_string(val)) { - cfg->home_chdir = true; - } - } - else if (!strcasecmp(name, "LOGIN_SHELL")) { - // execute a shell as a login shell - if (is_bool_string(val)) { - cfg->enable_loginshell = true; - } - } - else if (!strcasecmp(name, "SOCKET_TIMEOUT")) { - // telnet socket timeout - cfg->telsock_timeout = atoi(val); - } - else if (!strcasecmp(name, "SSH_AGENT_PROXY")) { - // ssh-agent proxy - if (is_bool_string(val)) { - cfg->enable_agent_proxy = true; - } - } - else if (!strcasecmp(name, "DEBUG")) { - // debug mode - if (is_bool_string(val)) { - cfg->debug_flag = true; - } - } -} - -typedef struct { - sh_env_data_t *env; -} cfg_private_data_t; - -static void destroy(cfg_data_t *cfg_data) -{ -// env_destry_all(cfg_data); - sh_env_t *sh_env = cfg_data->sh_env; - sh_env->destroy(sh_env); - cfg_private_data_t *pr_data = (cfg_private_data_t *)cfg_data->private_data; - free(pr_data); - free(cfg_data); -} - -// read each setting parameter -// configuration file (.cfg) path -static bool load_cfg(cfg_data_t *cfg_data, const char *conf) -{ - FILE* fp = fopen(conf, "r"); - if (fp == NULL) { - return false; - } - - char buf[BUFSIZ]; - while (fgets(buf, sizeof(buf), fp) != NULL) { - parse_cfg_line(buf, cfg_data); - } - fclose(fp); - - return true; -} - -#if !defined(offsetof) -#define offsetof(s,m) ((size_t)&(((s*)0)->m)) -#endif - -#if DUMP_ENABLE -static void dump(cfg_data_t *cfg_data, void (*print)(const char* msg, ...)) -{ - const static struct { - const char *name; - size_t offset; - char type; - } list[] = { - { "username", offsetof(cfg_data_t, username), 's' }, - { "term", offsetof(cfg_data_t, term), 's' }, - { "termopt", offsetof(cfg_data_t, termopt), 's' }, - { "shell", offsetof(cfg_data_t, shell), 's' }, - { "term_type", offsetof(cfg_data_t, term_type), 's' }, - { "change_dir", offsetof(cfg_data_t, change_dir), 's' }, - { "port_start", offsetof(cfg_data_t, port_start), 'i' }, - { "port_range", offsetof(cfg_data_t, port_range), 'i' }, - { "cl_port", offsetof(cfg_data_t, cl_port), 'i' }, - { "home_chdir", offsetof(cfg_data_t, home_chdir), 'b' }, - { "enable_loginshell", offsetof(cfg_data_t, enable_loginshell), 'b' }, - { "telsock_timeout", offsetof(cfg_data_t, telsock_timeout), 'i' }, - { "enable_agent_proxy", offsetof(cfg_data_t, enable_agent_proxy), 'b' }, - { "dumb", offsetof(cfg_data_t, dumb), 'b' }, - { "debug_flag", offsetof(cfg_data_t, debug_flag), 'b' }, - }; - for (int i = 0; i < (int)(sizeof(list)/sizeof(list[0])); i++) { - uint8_t *p = (uint8_t *)cfg_data + list[i].offset; - switch (list[i].type) { - case 's': { - char *str = *(char **)p; - print("%s=%s", list[i].name, str == NULL ? "NULL" : str); - break; - } - case 'i': { - int i2 = *(int *)p; - print("%s=%d(0x%x)", list[i].name, i2, i2); - break; - } - case 'b': { - bool b = *(bool *)p; - print("%s=%i(%s)", list[i].name, b, b ? "true" : "false"); - break; - } - default: - print("?"); - break; - } - } - - sh_env_t *sh_env = cfg_data->sh_env; - for(int i = 0;;i++) { - char *value; - const char *env = sh_env->get(sh_env, i, &value); - if (env == NULL) { - break; - } - print("env %d %s=%s", i, env, value); - } -} -#else -static void dump(cfg_data_t *cfg_data, void (*print)(const char* msg, ...)) -{ - (void)cfg_data; - (void)print; -} -#endif - -cfg_data_t *create_cfg(void) -{ - cfg_data_t *cfg_data = (cfg_data_t *)calloc(sizeof(*cfg_data), 1); - if (cfg_data == NULL) { - return NULL; - } - cfg_private_data_t *pr_data = (cfg_private_data_t *)calloc(sizeof(*pr_data), 1); - if (pr_data == NULL) { - free(cfg_data); - return NULL; - } - - // data, func - cfg_data->private_data = pr_data; - cfg_data->sh_env = create_sh_env(); - cfg_data->destroy = destroy; - cfg_data->load = load_cfg; - cfg_data->dump = dump; - - return cfg_data; -} Copied: trunk/cygwin/cygterm/cygterm_cfg.cpp (from rev 9793, trunk/cygwin/cygterm/cygterm_cfg.cc) =================================================================== --- trunk/cygwin/cygterm/cygterm_cfg.cpp (rev 0) +++ trunk/cygwin/cygterm/cygterm_cfg.cpp 2022-03-08 15:06:52 UTC (rev 9794) @@ -0,0 +1,406 @@ +/* + * (C) 2022- TeraTerm Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <stdlib.h> + +#include "cygterm_cfg.h" + +#define DUMP_ENABLE 1 + +// additional env vars given to a shell +//------------------------------------- + +typedef struct sh_env_data_t { + struct sh_env_data_t* next; + char *name; + char *value; +} sh_env_data_t; + +typedef struct sh_env_private_tag { + sh_env_data_t *envp; +} sh_env_private_t; + +static bool env_add(sh_env_t *envp, const char* name, const char* value) +{ + sh_env_private_t *pr_data = (sh_env_private_t *)envp->private_data; + sh_env_data_t* e; + + if (name[0] == 0) { + return true; + } + e = (sh_env_data_t*)malloc(sizeof(*e)); + if (e == NULL) { + return false; + } + + e->name = strdup(name); + e->value = strdup(value); + e->next = NULL; + + if (pr_data->envp == NULL) { + pr_data->envp = e; + return true; + } + sh_env_data_t* env_data = pr_data->envp; + sh_env_data_t* prev_env = NULL; + while(1) { + if (strcmp(env_data->name, name) == 0) { + // \x93\xAF\x82\xB6\x96\xBC\x91O -> \x93\xFC\x82\xEA\x91ւ\xA6 + if (prev_env == NULL) { + pr_data->envp = e; + } else { + prev_env->next = e; + e->next = env_data->next; + } + free(env_data->name); + free(env_data->value); + free(env_data); + break; + } + if (env_data->next == NULL) { + // \x8DŌ\xE3\x82܂ŗ\x88\x82\xBD + env_data->next = e; + break; + } + prev_env = env_data; + env_data = env_data->next; + } + + return true; +} + +static bool env_add1(sh_env_t *envp, const char* name_value) +{ + if (name_value[0] == 0) { + return true; + } + char *name = strdup(name_value); + char *p = strchr(name, '='); + char *value; + if (p == NULL) { + value = NULL; + } + else { + *p = 0; + value = strdup(p+1); + } + bool r = env_add(envp, name, value); + free(value); + free(name); + return r; +} + +static char *env_get(sh_env_t *envp, int index, char **value) +{ + sh_env_private_t *pr_data = (sh_env_private_t *)envp->private_data; + sh_env_data_t* e = pr_data->envp; + if (e == NULL) { + return NULL; + } + while(1) { + if (index == 0) { + *value = e->value; + return e->name;; + } + if (e->next == NULL) { + *value = NULL; + return NULL; + } + index--; + e = e->next; + } +} + +static void env_destry_all(sh_env_t *envp) +{ + sh_env_private_t *pr_data = (sh_env_private_t *)envp->private_data; + sh_env_data_t* e = pr_data->envp; + if (e == NULL) { + return; + } + pr_data->envp = NULL; + while(1) { + sh_env_data_t* e_next = e->next; + e->next = NULL; + free(e); + e = e_next; + if (e == NULL) { + break; + } + } +} + +static void env_destry(sh_env_t *envp) +{ + sh_env_private_t *pr_data = (sh_env_private_t *)envp->private_data; + env_destry_all(envp); + free(pr_data); + envp->private_data = NULL; + free(envp); +} + +sh_env_t *create_sh_env(void) +{ + sh_env_t *sh_env = (sh_env_t *)calloc(sizeof(*sh_env), 1); + if (sh_env == NULL) { + return NULL; + } + sh_env_private_t *pr_data = (sh_env_private_t *)calloc(sizeof(*pr_data), 1); + if (pr_data == NULL) { + free(sh_env); + return NULL; + } + sh_env->private_data = pr_data; + sh_env->destroy = env_destry; + sh_env->add = env_add; + sh_env->add1 = env_add1; + sh_env->get = env_get; + + return sh_env; +} + +static bool is_bool_string(const char *s) +{ + if (strchr("YyTt", *s) != NULL) + return true; + if (atoi(s) > 0) + return true; + + return false; +} + +//==================================// +// parse line in configuration file // +//----------------------------------// +static void parse_cfg_line(char *buf, cfg_data_t *cfg) +{ + // "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)) { + return; // 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 != '=') { + return; // igonore line without '=' + } + for (++p2; isspace(*p2); ++p2); + char* val = p2; + for (p2 += strlen(p2); isspace(*(p2-1)); --p2); + *p1 = *p2 = 0; + + if (!strcasecmp(name, "TERM")) { + // terminal emulator command line (host:%s, port#:%d) + free(cfg->term); + cfg->term = strdup(val); + } + else if (!strcasecmp(name, "SHELL")) { + // shell command line + if (strcasecmp(val, "AUTO") != 0) { + free(cfg->shell); + cfg->shell = strdup(val); + } + } + else if (!strcasecmp(name, "PORT_START")) { + // minimum port# for TELNET + cfg->port_start = atoi(val); + } + else if (!strcasecmp(name, "PORT_RANGE")) { + // number of ports for TELNET + cfg->port_range = atoi(val); + } + else if (!strcasecmp(name, "TERM_TYPE")) { + // terminal type name (maybe overridden by TELNET negotiation.) + free(cfg->term_type); + cfg->term_type = strdup(val); + } + else if (!strncasecmp(name, "ENV_", 4)) { + // additional env vars given to a shell + sh_env_t *sh_env = cfg->sh_env; + sh_env->add1(sh_env, val); + } + else if (!strcasecmp(name, "HOME_CHDIR")) { + // change directory to home + if (is_bool_string(val)) { + cfg->home_chdir = true; + } + } + else if (!strcasecmp(name, "LOGIN_SHELL")) { + // execute a shell as a login shell + if (is_bool_string(val)) { + cfg->enable_loginshell = true; + } + } + else if (!strcasecmp(name, "SOCKET_TIMEOUT")) { + // telnet socket timeout + cfg->telsock_timeout = atoi(val); + } + else if (!strcasecmp(name, "SSH_AGENT_PROXY")) { + // ssh-agent proxy + if (is_bool_string(val)) { + cfg->enable_agent_proxy = true; + } + } + else if (!strcasecmp(name, "DEBUG")) { + // debug mode + if (is_bool_string(val)) { + cfg->debug_flag = true; + } + } +} + +typedef struct { + sh_env_data_t *env; +} cfg_private_data_t; + +static void destroy(cfg_data_t *cfg_data) +{ +// env_destry_all(cfg_data); + sh_env_t *sh_env = cfg_data->sh_env; + sh_env->destroy(sh_env); + cfg_private_data_t *pr_data = (cfg_private_data_t *)cfg_data->private_data; + free(pr_data); + free(cfg_data); +} + +// read each setting parameter +// configuration file (.cfg) path +static bool load_cfg(cfg_data_t *cfg_data, const char *conf) +{ + FILE* fp = fopen(conf, "r"); + if (fp == NULL) { + return false; + } + + char buf[BUFSIZ]; + while (fgets(buf, sizeof(buf), fp) != NULL) { + parse_cfg_line(buf, cfg_data); + } + fclose(fp); + + return true; +} + +#if !defined(offsetof) +#define offsetof(s,m) ((size_t)&(((s*)0)->m)) +#endif + +#if DUMP_ENABLE +static void dump(cfg_data_t *cfg_data, void (*print)(const char* msg, ...)) +{ + const static struct { + const char *name; + size_t offset; + char type; + } list[] = { + { "username", offsetof(cfg_data_t, username), 's' }, + { "term", offsetof(cfg_data_t, term), 's' }, + { "termopt", offsetof(cfg_data_t, termopt), 's' }, + { "shell", offsetof(cfg_data_t, shell), 's' }, + { "term_type", offsetof(cfg_data_t, term_type), 's' }, + { "change_dir", offsetof(cfg_data_t, change_dir), 's' }, + { "port_start", offsetof(cfg_data_t, port_start), 'i' }, + { "port_range", offsetof(cfg_data_t, port_range), 'i' }, + { "cl_port", offsetof(cfg_data_t, cl_port), 'i' }, + { "home_chdir", offsetof(cfg_data_t, home_chdir), 'b' }, + { "enable_loginshell", offsetof(cfg_data_t, enable_loginshell), 'b' }, + { "telsock_timeout", offsetof(cfg_data_t, telsock_timeout), 'i' }, + { "enable_agent_proxy", offsetof(cfg_data_t, enable_agent_proxy), 'b' }, + { "dumb", offsetof(cfg_data_t, dumb), 'b' }, + { "debug_flag", offsetof(cfg_data_t, debug_flag), 'b' }, + }; + for (int i = 0; i < (int)(sizeof(list)/sizeof(list[0])); i++) { + uint8_t *p = (uint8_t *)cfg_data + list[i].offset; + switch (list[i].type) { + case 's': { + char *str = *(char **)p; + print("%s=%s", list[i].name, str == NULL ? "NULL" : str); + break; + } + case 'i': { + int i2 = *(int *)p; + print("%s=%d(0x%x)", list[i].name, i2, i2); + break; + } + case 'b': { + bool b = *(bool *)p; + print("%s=%i(%s)", list[i].name, b, b ? "true" : "false"); + break; + } + default: + print("?"); + break; + } + } + + sh_env_t *sh_env = cfg_data->sh_env; + for(int i = 0;;i++) { + char *value; + const char *env = sh_env->get(sh_env, i, &value); + if (env == NULL) { + break; + } + print("env %d %s=%s", i, env, value); + } +} +#else +static void dump(cfg_data_t *cfg_data, void (*print)(const char* msg, ...)) +{ + (void)cfg_data; + (void)print; +} +#endif + +cfg_data_t *create_cfg(void) +{ + cfg_data_t *cfg_data = (cfg_data_t *)calloc(sizeof(*cfg_data), 1); + if (cfg_data == NULL) { + return NULL; + } + cfg_private_data_t *pr_data = (cfg_private_data_t *)calloc(sizeof(*pr_data), 1); + if (pr_data == NULL) { + free(cfg_data); + return NULL; + } + + // data, func + cfg_data->private_data = pr_data; + cfg_data->sh_env = create_sh_env(); + cfg_data->destroy = destroy; + cfg_data->load = load_cfg; + cfg_data->dump = dump; + + return cfg_data; +}