prototype: ncurses window class wrapping a PTY
This commit is contained in:
commit
28643632ce
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
build/
|
||||
sandbox/
|
33
.vscode/launch.json
vendored
Normal file
33
.vscode/launch.json
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "(gdb) cursesPty starten",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/NCursesPtyApp",
|
||||
"args": [],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${workspaceFolder}",
|
||||
"environment": [],
|
||||
"externalConsole": true,
|
||||
"MIMode": "gdb",
|
||||
"setupCommands": [
|
||||
{
|
||||
"description": "Automatische Strukturierung und Einrückung für \"gdb\" aktivieren",
|
||||
"text": "-enable-pretty-printing",
|
||||
"ignoreFailures": true
|
||||
},
|
||||
{
|
||||
"description": "Disassemblierungsvariante auf Intel festlegen",
|
||||
"text": "-gdb-set disassembly-flavor intel",
|
||||
"ignoreFailures": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
]
|
||||
}
|
57
.vscode/settings.json
vendored
Normal file
57
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,57 @@
|
|||
{
|
||||
"cmake.configureOnOpen": true,
|
||||
"files.associations": {
|
||||
"*.tcc": "cpp",
|
||||
"fstream": "cpp",
|
||||
"iosfwd": "cpp",
|
||||
"istream": "cpp",
|
||||
"limits": "cpp",
|
||||
"sstream": "cpp",
|
||||
"streambuf": "cpp",
|
||||
"filesystem": "cpp",
|
||||
"cstdio": "cpp",
|
||||
"thread": "cpp",
|
||||
"exception": "cpp",
|
||||
"memory_resource": "cpp",
|
||||
"new": "cpp",
|
||||
"ostream": "cpp",
|
||||
"array": "cpp",
|
||||
"atomic": "cpp",
|
||||
"bit": "cpp",
|
||||
"cctype": "cpp",
|
||||
"chrono": "cpp",
|
||||
"clocale": "cpp",
|
||||
"cmath": "cpp",
|
||||
"codecvt": "cpp",
|
||||
"cstdarg": "cpp",
|
||||
"cstddef": "cpp",
|
||||
"cstdint": "cpp",
|
||||
"cstdlib": "cpp",
|
||||
"cstring": "cpp",
|
||||
"ctime": "cpp",
|
||||
"cwchar": "cpp",
|
||||
"cwctype": "cpp",
|
||||
"deque": "cpp",
|
||||
"unordered_map": "cpp",
|
||||
"vector": "cpp",
|
||||
"algorithm": "cpp",
|
||||
"functional": "cpp",
|
||||
"iterator": "cpp",
|
||||
"memory": "cpp",
|
||||
"numeric": "cpp",
|
||||
"optional": "cpp",
|
||||
"random": "cpp",
|
||||
"ratio": "cpp",
|
||||
"string": "cpp",
|
||||
"string_view": "cpp",
|
||||
"system_error": "cpp",
|
||||
"tuple": "cpp",
|
||||
"type_traits": "cpp",
|
||||
"utility": "cpp",
|
||||
"initializer_list": "cpp",
|
||||
"iomanip": "cpp",
|
||||
"iostream": "cpp",
|
||||
"stdexcept": "cpp",
|
||||
"typeinfo": "cpp"
|
||||
}
|
||||
}
|
31
CMakeLists.txt
Normal file
31
CMakeLists.txt
Normal file
|
@ -0,0 +1,31 @@
|
|||
cmake_minimum_required(VERSION 3.16.3)
|
||||
project(NCursesPty VERSION 0.1.0)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
include(CTest)
|
||||
enable_testing()
|
||||
|
||||
include("cmake/ncurses.cmake")
|
||||
include("cmake/gsl.cmake")
|
||||
|
||||
|
||||
### libraries
|
||||
find_library(LIBVTERM_LIBRARY vterm)
|
||||
find_library(UTIL_LIBRARY util)
|
||||
|
||||
add_library(NCursesPty NCursesPtyWindow.cpp Debug.cpp)
|
||||
target_link_libraries(NCursesPty ${CURSES_LIBRARIES} ${LIBVTERM_LIBRARY} ${UTIL_LIBRARY})
|
||||
|
||||
### threads
|
||||
set(THREADS_PREFER_PTHREAD_FLAG true)
|
||||
find_package(Threads REQUIRED)
|
||||
target_link_libraries(NCursesPty Threads::Threads)
|
||||
|
||||
### demo application
|
||||
add_executable(NCursesPtyApp main.cpp NCursesPtyApp.cpp)
|
||||
target_link_libraries(NCursesPtyApp ${CURSES_LIBRARIES} NCursesPty)
|
||||
|
||||
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
|
||||
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
|
||||
include(CPack)
|
337
Debug.cpp
Normal file
337
Debug.cpp
Normal file
|
@ -0,0 +1,337 @@
|
|||
/**
|
||||
* @author Christian Burger (christian@krikkel.de)
|
||||
* @todo Switch over to <seccomp.h>? For resolving system call numbers?
|
||||
* Maybe keep the current solution as a fallback?
|
||||
* @todo catch `out of range` exception from stoi()
|
||||
*
|
||||
* Contains mapping of system calls numbers to names from original dev system:
|
||||
* "Linux 5.13.0-28-generic #31~20.04.1-Ubuntu SMP \
|
||||
* Wed Jan 19 14:08:10 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux"
|
||||
* See comment at the end for the actual command to gather mapping information.
|
||||
*/
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
#include "Debug.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <cstdio>
|
||||
|
||||
#define BUFFER_SIZE 128
|
||||
|
||||
namespace krikkel
|
||||
{
|
||||
using std::filesystem::is_directory;
|
||||
|
||||
Debug::Debug()
|
||||
{
|
||||
string directory = ".";
|
||||
if(is_directory("sandbox"))
|
||||
directory = "./sandbox";
|
||||
logFile = fstream(directory + "/debug.log", fstream::out | fstream::app);//| fstream::trunc);
|
||||
logFile << endl << endl << "New instance of class Debug." << endl;
|
||||
logFile << getUname();
|
||||
logFile.flush();
|
||||
}
|
||||
|
||||
string Debug::getUname()
|
||||
{
|
||||
string result = "";
|
||||
char buffer[BUFFER_SIZE];
|
||||
FILE *unameProcess = popen("uname -srvmpio", "r");
|
||||
|
||||
if(unameProcess && fgets(buffer, BUFFER_SIZE, unameProcess))
|
||||
result = buffer;
|
||||
pclose(unameProcess);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
string Debug::getTimestamp()
|
||||
{
|
||||
time_t now = std::time(NULL);
|
||||
tm *localNow = localtime(&now);
|
||||
static char formattedLocalNow[32];
|
||||
strftime(formattedLocalNow, 32, "%c", localNow);
|
||||
|
||||
return formattedLocalNow;
|
||||
}
|
||||
|
||||
void Debug::log(string message, string fileName, int lineNo, string functionName)
|
||||
{
|
||||
string output = "";
|
||||
size_t position = message.find("sysCall(");
|
||||
|
||||
if(position != string::npos)
|
||||
{
|
||||
size_t end = message.find_first_not_of("0123456789", position + 8);
|
||||
unsigned long long systemCallNumber = std::stoi(message.substr(position + 8, end - position - 8));
|
||||
string systemCallName = strlen(syscalls[systemCallNumber]) != 0 ? syscalls[systemCallNumber]
|
||||
: "unknown_system_call";
|
||||
if(systemCallNumber < MAX_NUMBER_OF_SYSCALLS)
|
||||
output = message.replace(position, end - position + 1, systemCallName + "(");
|
||||
}
|
||||
if(output == "")
|
||||
output = message;
|
||||
|
||||
logFile << getTimestamp() << ": " << output << " (in "
|
||||
<< fileName.substr(fileName.rfind('/') + 1) << ":"
|
||||
<< lineNo << " in " << functionName << "())" << endl;
|
||||
logFile.flush();
|
||||
}
|
||||
|
||||
Debug *Debug::getInstance()
|
||||
{
|
||||
static Debug *debug = new Debug();
|
||||
return debug;
|
||||
}
|
||||
|
||||
const char *Debug::syscalls[MAX_NUMBER_OF_SYSCALLS] = {[0] = "read",[1] = "write",
|
||||
[2] = "open",[3] = "close",
|
||||
[4] = "stat",[5] = "fstat",
|
||||
[6] = "lstat",[7] = "poll",
|
||||
[8] = "lseek",[9] = "mmap",
|
||||
[10] = "mprotect",[11] = "munmap",
|
||||
[12] = "brk",[13] = "rt_sigaction",
|
||||
[14] = "rt_sigprocmask",[15] = "rt_sigreturn",
|
||||
[16] = "ioctl",[17] = "pread64",
|
||||
[18] = "pwrite64",[19] = "readv",
|
||||
[20] = "writev",[21] = "access",
|
||||
[22] = "pipe",[23] = "select",
|
||||
[24] = "sched_yield",[25] = "mremap",
|
||||
[26] = "msync",[27] = "mincore",
|
||||
[28] = "madvise",[29] = "shmget",
|
||||
[30] = "shmat",[31] = "shmctl",
|
||||
[32] = "dup",[33] = "dup2",
|
||||
[34] = "pause",[35] = "nanosleep",
|
||||
[36] = "getitimer",[37] = "alarm",
|
||||
[38] = "setitimer",[39] = "getpid",
|
||||
[40] = "sendfile",[41] = "socket",
|
||||
[42] = "connect",[43] = "accept",
|
||||
[44] = "sendto",[45] = "recvfrom",
|
||||
[46] = "sendmsg",[47] = "recvmsg",
|
||||
[48] = "shutdown",[49] = "bind",
|
||||
[50] = "listen",[51] = "getsockname",
|
||||
[52] = "getpeername",[53] = "socketpair",
|
||||
[54] = "setsockopt",[55] = "getsockopt",
|
||||
[56] = "clone",[57] = "fork",
|
||||
[58] = "vfork",[59] = "execve",
|
||||
[60] = "exit",[61] = "wait4",
|
||||
[62] = "kill",[63] = "uname",
|
||||
[64] = "semget",[65] = "semop",
|
||||
[66] = "semctl",[67] = "shmdt",
|
||||
[68] = "msgget",[69] = "msgsnd",
|
||||
[70] = "msgrcv",[71] = "msgctl",
|
||||
[72] = "fcntl",[73] = "flock",
|
||||
[74] = "fsync",[75] = "fdatasync",
|
||||
[76] = "truncate",[77] = "ftruncate",
|
||||
[78] = "getdents",[79] = "getcwd",
|
||||
[80] = "chdir",[81] = "fchdir",
|
||||
[82] = "rename",[83] = "mkdir",
|
||||
[84] = "rmdir",[85] = "creat",
|
||||
[86] = "link",[87] = "unlink",
|
||||
[88] = "symlink",[89] = "readlink",
|
||||
[90] = "chmod",[91] = "fchmod",
|
||||
[92] = "chown",[93] = "fchown",
|
||||
[94] = "lchown",[95] = "umask",
|
||||
[96] = "gettimeofday",[97] = "getrlimit",
|
||||
[98] = "getrusage",[99] = "sysinfo",
|
||||
[100] = "times",[101] = "ptrace",
|
||||
[102] = "getuid",[103] = "syslog",
|
||||
[104] = "getgid",[105] = "setuid",
|
||||
[106] = "setgid",[107] = "geteuid",
|
||||
[108] = "getegid",[109] = "setpgid",
|
||||
[110] = "getppid",[111] = "getpgrp",
|
||||
[112] = "setsid",[113] = "setreuid",
|
||||
[114] = "setregid",[115] = "getgroups",
|
||||
[116] = "setgroups",[117] = "setresuid",
|
||||
[118] = "getresuid",[119] = "setresgid",
|
||||
[120] = "getresgid",[121] = "getpgid",
|
||||
[122] = "setfsuid",[123] = "setfsgid",
|
||||
[124] = "getsid",[125] = "capget",
|
||||
[126] = "capset",[127] = "rt_sigpending",
|
||||
[128] = "rt_sigtimedwait",[129] = "rt_sigqueueinfo",
|
||||
[130] = "rt_sigsuspend",[131] = "sigaltstack",
|
||||
[132] = "utime",[133] = "mknod",
|
||||
[134] = "uselib",[135] = "personality",
|
||||
[136] = "ustat",[137] = "statfs",
|
||||
[138] = "fstatfs",[139] = "sysfs",
|
||||
[140] = "getpriority",[141] = "setpriority",
|
||||
[142] = "sched_setparam",[143] = "sched_getparam",
|
||||
[144] = "sched_setscheduler",[145] = "sched_getscheduler",
|
||||
[146] = "sched_get_priority_max",[147] = "sched_get_priority_min",
|
||||
[148] = "sched_rr_get_interval",[149] = "mlock",
|
||||
[150] = "munlock",[151] = "mlockall",
|
||||
[152] = "munlockall",[153] = "vhangup",
|
||||
[154] = "modify_ldt",[155] = "pivot_root",
|
||||
[156] = "",[157] = "prctl",
|
||||
[158] = "arch_prctl",[159] = "adjtimex",
|
||||
[160] = "setrlimit",[161] = "chroot",
|
||||
[162] = "sync",[163] = "acct",
|
||||
[164] = "settimeofday",[165] = "mount",
|
||||
[166] = "",[167] = "swapon",
|
||||
[168] = "swapoff",[169] = "reboot",
|
||||
[170] = "sethostname",[171] = "setdomainname",
|
||||
[172] = "iopl",[173] = "ioperm",
|
||||
[174] = "",[175] = "init_module",
|
||||
[176] = "delete_module",[177] = "",
|
||||
[178] = "",[179] = "quotactl",
|
||||
[180] = "",[181] = "",
|
||||
[182] = "",[183] = "",
|
||||
[184] = "",[185] = "",
|
||||
[186] = "gettid",[187] = "readahead",
|
||||
[188] = "setxattr",[189] = "lsetxattr",
|
||||
[190] = "fsetxattr",[191] = "getxattr",
|
||||
[192] = "lgetxattr",[193] = "fgetxattr",
|
||||
[194] = "listxattr",[195] = "llistxattr",
|
||||
[196] = "flistxattr",[197] = "removexattr",
|
||||
[198] = "lremovexattr",[199] = "fremovexattr",
|
||||
[200] = "tkill",[201] = "time",
|
||||
[202] = "futex",[203] = "sched_setaffinity",
|
||||
[204] = "sched_getaffinity",[205] = "set_thread_area",
|
||||
[206] = "io_setup",[207] = "io_destroy",
|
||||
[208] = "io_getevents",[209] = "io_submit",
|
||||
[210] = "io_cancel",[211] = "get_thread_area",
|
||||
[212] = "",[213] = "epoll_create",
|
||||
[214] = "",[215] = "",
|
||||
[216] = "remap_file_pages",[217] = "getdents64",
|
||||
[218] = "set_tid_address",[219] = "restart_syscall",
|
||||
[220] = "semtimedop",[221] = "fadvise64",
|
||||
[222] = "timer_create",[223] = "timer_settime",
|
||||
[224] = "timer_gettime",[225] = "timer_getoverrun",
|
||||
[226] = "timer_delete",[227] = "clock_settime",
|
||||
[228] = "clock_gettime",[229] = "clock_getres",
|
||||
[230] = "clock_nanosleep",[231] = "exit_group",
|
||||
[232] = "epoll_wait",[233] = "epoll_ctl",
|
||||
[234] = "tgkill",[235] = "utimes",
|
||||
[236] = "",[237] = "mbind",
|
||||
[238] = "set_mempolicy",[239] = "get_mempolicy",
|
||||
[240] = "mq_open",[241] = "mq_unlink",
|
||||
[242] = "mq_timedsend",[243] = "mq_timedreceive",
|
||||
[244] = "mq_notify",[245] = "mq_getsetattr",
|
||||
[246] = "kexec_load",[247] = "waitid",
|
||||
[248] = "add_key",[249] = "request_key",
|
||||
[250] = "keyctl",[251] = "ioprio_set",
|
||||
[252] = "ioprio_get",[253] = "inotify_init",
|
||||
[254] = "inotify_add_watch",[255] = "inotify_rm_watch",
|
||||
[256] = "migrate_pages",[257] = "openat",
|
||||
[258] = "mkdirat",[259] = "mknodat",
|
||||
[260] = "fchownat",[261] = "futimesat",
|
||||
[262] = "newfstatat",[263] = "unlinkat",
|
||||
[264] = "renameat",[265] = "linkat",
|
||||
[266] = "symlinkat",[267] = "readlinkat",
|
||||
[268] = "fchmodat",[269] = "faccessat",
|
||||
[270] = "pselect6",[271] = "ppoll",
|
||||
[272] = "unshare",[273] = "set_robust_list",
|
||||
[274] = "get_robust_list",[275] = "splice",
|
||||
[276] = "tee",[277] = "sync_file_range",
|
||||
[278] = "vmsplice",[279] = "move_pages",
|
||||
[280] = "utimensat",[281] = "epoll_pwait",
|
||||
[282] = "signalfd",[283] = "timerfd_create",
|
||||
[284] = "eventfd",[285] = "fallocate",
|
||||
[286] = "timerfd_settime",[287] = "timerfd_gettime",
|
||||
[288] = "accept4",[289] = "signalfd4",
|
||||
[290] = "eventfd2",[291] = "epoll_create1",
|
||||
[292] = "dup3",[293] = "pipe2",
|
||||
[294] = "inotify_init1",[295] = "preadv",
|
||||
[296] = "pwritev",[297] = "rt_tgsigqueueinfo",
|
||||
[298] = "perf_event_open",[299] = "recvmmsg",
|
||||
[300] = "fanotify_init",[301] = "fanotify_mark",
|
||||
[302] = "prlimit64",[303] = "name_to_handle_at",
|
||||
[304] = "open_by_handle_at",[305] = "clock_adjtime",
|
||||
[306] = "syncfs",[307] = "sendmmsg",
|
||||
[308] = "setns",[309] = "getcpu",
|
||||
[310] = "process_vm_readv",[311] = "process_vm_writev",
|
||||
[312] = "kcmp",[313] = "finit_module",
|
||||
[314] = "sched_setattr",[315] = "sched_getattr",
|
||||
[316] = "renameat2",[317] = "seccomp",
|
||||
[318] = "getrandom",[319] = "memfd_create",
|
||||
[320] = "kexec_file_load",[321] = "bpf",
|
||||
[322] = "execveat",[323] = "userfaultfd",
|
||||
[324] = "membarrier",[325] = "mlock2",
|
||||
[326] = "copy_file_range",[327] = "preadv2",
|
||||
[328] = "pwritev2",[329] = "pkey_mprotect",
|
||||
[330] = "pkey_alloc",[331] = "pkey_free",
|
||||
[332] = "statx",[333] = "io_pgetevents",
|
||||
[334] = "rseq",[335] = "",
|
||||
[336] = "",[337] = "",
|
||||
[338] = "",[339] = "",
|
||||
[340] = "",[341] = "",
|
||||
[342] = "",[343] = "",
|
||||
[344] = "",[345] = "",
|
||||
[346] = "",[347] = "",
|
||||
[348] = "",[349] = "",
|
||||
[350] = "",[351] = "",
|
||||
[352] = "",[353] = "",
|
||||
[354] = "",[355] = "",
|
||||
[356] = "",[357] = "",
|
||||
[358] = "",[359] = "",
|
||||
[360] = "",[361] = "",
|
||||
[362] = "",[363] = "",
|
||||
[364] = "",[365] = "",
|
||||
[366] = "",[367] = "",
|
||||
[368] = "",[369] = "",
|
||||
[370] = "",[371] = "",
|
||||
[372] = "",[373] = "",
|
||||
[374] = "",[375] = "",
|
||||
[376] = "",[377] = "",
|
||||
[378] = "",[379] = "",
|
||||
[380] = "",[381] = "",
|
||||
[382] = "",[383] = "",
|
||||
[384] = "",[385] = "",
|
||||
[386] = "",[387] = "",
|
||||
[388] = "",[389] = "",
|
||||
[390] = "",[391] = "",
|
||||
[392] = "",[393] = "",
|
||||
[394] = "",[395] = "",
|
||||
[396] = "",[397] = "",
|
||||
[398] = "",[399] = "",
|
||||
[400] = "",[401] = "",
|
||||
[402] = "",[403] = "",
|
||||
[404] = "",[405] = "",
|
||||
[406] = "",[407] = "",
|
||||
[408] = "",[409] = "",
|
||||
[410] = "",[411] = "",
|
||||
[412] = "",[413] = "",
|
||||
[414] = "",[415] = "",
|
||||
[416] = "",[417] = "",
|
||||
[418] = "",[419] = "",
|
||||
[420] = "",[421] = "",
|
||||
[422] = "",[423] = "",
|
||||
[424] = "pidfd_send_signal",[425] = "io_uring_setup",
|
||||
[426] = "io_uring_enter",[427] = "io_uring_register",
|
||||
[428] = "open_tree",[429] = "move_mount",
|
||||
[430] = "fsopen",[431] = "fsconfig",
|
||||
[432] = "fsmount",[433] = "fspick",
|
||||
[434] = "pidfd_open",[435] = "clone3" };
|
||||
}
|
||||
|
||||
#endif
|
||||
/*
|
||||
|
||||
Following command in part thx to:
|
||||
https://unix.stackexchange.com/questions/445507/syscall-number-%E2%86%92-name-mapping-at-runtime
|
||||
|
||||
Command to create mapping of system call number to system call name:
|
||||
|
||||
#!/bin/bash
|
||||
|
||||
awk 'BEGIN { print "#include <sys/syscall.h>" }
|
||||
/p_syscall_meta/ { syscall = substr($NF, 19);
|
||||
printf "[SYS_%s] = \"%s\", \n", syscall, syscall }' /proc/kallsyms \
|
||||
| gcc -E -P - \
|
||||
| sort -V \
|
||||
| grep "\[[0-9]" \
|
||||
| awk 'BEGIN {expectedIndex = 0;}
|
||||
{ actualIndex = $1;
|
||||
gsub(/[\[\]]/, "", actualIndex);
|
||||
if (actualIndex != expectedIndex)
|
||||
for (; expectedIndex < actualIndex; expectedIndex++)
|
||||
print "[" expectedIndex "] = \"\",";
|
||||
print $0; expectedIndex++ }' \
|
||||
| tr -d "\n" \
|
||||
| sed -e "s/^/const char *syscalls[1024] = {/; s/,$/ };/" \
|
||||
| sed -e 's/,/,\n /2;P;D' \
|
||||
| cat - <(echo) # new line at the end
|
||||
*/
|
52
Debug.hpp
Normal file
52
Debug.hpp
Normal file
|
@ -0,0 +1,52 @@
|
|||
/**
|
||||
* @brief Writes messages to `debug.log` if `NDEBUG` is not defined.
|
||||
* @author Christian Burger (christian@krikkel.de)
|
||||
*/
|
||||
|
||||
#ifndef __DEBUG_H__
|
||||
#define __DEBUG_H__
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <ctime>
|
||||
|
||||
namespace krikkel
|
||||
{
|
||||
using std::fstream;
|
||||
using std::string;
|
||||
using std::endl;
|
||||
using std::strftime;
|
||||
using std::localtime;
|
||||
using std::time;
|
||||
|
||||
class Debug
|
||||
{
|
||||
private:
|
||||
fstream logFile;
|
||||
static const int MAX_NUMBER_OF_SYSCALLS = 1024;
|
||||
static const char * syscalls[MAX_NUMBER_OF_SYSCALLS];
|
||||
|
||||
Debug();
|
||||
string getTimestamp();
|
||||
string getUname();
|
||||
|
||||
public:
|
||||
void log(string message, string fileName, int lineNo
|
||||
, string functionName);
|
||||
static Debug *getInstance();
|
||||
};
|
||||
}
|
||||
|
||||
#define __debug_log(message) krikkel::Debug::getInstance()\
|
||||
->log(message, __FILE__, __LINE__, __func__)
|
||||
|
||||
#else
|
||||
|
||||
/// @todo check what's the business with this "((void)0)" instead of empty macro
|
||||
#define __debug_log(message)
|
||||
|
||||
#endif /* NDEBUG */
|
||||
|
||||
#endif // __DEBUG_H__
|
41
NCursesPtyApp.cpp
Normal file
41
NCursesPtyApp.cpp
Normal file
|
@ -0,0 +1,41 @@
|
|||
/**
|
||||
* @author Christian Burger (christian@krikkel.de)
|
||||
*/
|
||||
|
||||
#include "NCursesPtyApp.hpp"
|
||||
#include "NCursesPtyWindow.hpp"
|
||||
#include "Debug.hpp"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <utmp.h>
|
||||
|
||||
namespace krikkel::NCursesPty
|
||||
{
|
||||
int NCursesPtyApp::run()
|
||||
{
|
||||
NCursesPtyWindow *ptyWindow = new NCursesPtyWindow(
|
||||
Root_Window->lines()
|
||||
, Root_Window->cols()
|
||||
, 0
|
||||
, 0);
|
||||
|
||||
if(fork() == 0)
|
||||
{
|
||||
/// @todo close(ptyWindow->getFdPtyHost()); ???
|
||||
login_tty(ptyWindow->getFdPtyClient());
|
||||
const int sizeArg = 2;
|
||||
const char *arg[sizeArg] = {"/bin/bash", NULL};
|
||||
|
||||
execv("/bin/bash", const_cast<char * const *>(arg));
|
||||
}
|
||||
|
||||
int input;
|
||||
while((input = ptyWindow->getch()) != 27)
|
||||
{
|
||||
__debug_log("input: " + std::to_string(input));
|
||||
ptyWindow->writeToClient((unsigned char) input);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
19
NCursesPtyApp.hpp
Normal file
19
NCursesPtyApp.hpp
Normal file
|
@ -0,0 +1,19 @@
|
|||
/**
|
||||
* @brief demo application for the library
|
||||
* @author Christian Burger (christian@krikkel.de)
|
||||
*/
|
||||
|
||||
#ifndef A3B2AE4E_0A39_468C_8CCA_E6508166702A
|
||||
#define A3B2AE4E_0A39_468C_8CCA_E6508166702A
|
||||
|
||||
#include <cursesapp.h>
|
||||
|
||||
namespace krikkel::NCursesPty
|
||||
{
|
||||
class NCursesPtyApp : public NCursesApplication
|
||||
{
|
||||
int run() override;
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* A3B2AE4E_0A39_468C_8CCA_E6508166702A */
|
283
NCursesPtyWindow.cpp
Normal file
283
NCursesPtyWindow.cpp
Normal file
|
@ -0,0 +1,283 @@
|
|||
/**
|
||||
* @author Christian Burger (christian@krikkel.de)
|
||||
*/
|
||||
|
||||
#include "NCursesPtyWindow.hpp"
|
||||
#include "Debug.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
#include <unistd.h>
|
||||
#include <sys/select.h>
|
||||
#include <gsl/gsl>
|
||||
#include <cctype>
|
||||
#include <termios.h>
|
||||
|
||||
namespace krikkel::NCursesPty
|
||||
{
|
||||
using gsl::narrow;
|
||||
|
||||
NCursesPtyWindow::NCursesPtyWindow(int lines, int columns, int y, int x)
|
||||
: NCursesWindow(lines, columns, y, x)
|
||||
{
|
||||
// to get the original terminal we need to shutdown ncurses for a moment
|
||||
::endwin();
|
||||
tcgetattr(STDIN_FILENO, &terminalParameters);
|
||||
::refresh();
|
||||
|
||||
winsize windowSize =
|
||||
{
|
||||
.ws_row = narrow<short unsigned int>(this->height())
|
||||
, .ws_col = narrow<short unsigned int>(this->width())
|
||||
};
|
||||
openpty(&fdPtyHost, &fdPtyClient, NULL, &terminalParameters, &windowSize);
|
||||
|
||||
pseudoTerminal = vterm_new(windowSize.ws_row, windowSize.ws_col);
|
||||
__debug_log("window size (x: " + std::to_string(windowSize.ws_col) + ", y: " + std::to_string(windowSize.ws_row) + ")");
|
||||
vterm_set_utf8(pseudoTerminal, true);
|
||||
pseudoTerminalScreen = vterm_obtain_screen(pseudoTerminal);
|
||||
vterm_screen_reset(pseudoTerminalScreen, true);
|
||||
vterm_screen_set_callbacks(pseudoTerminalScreen, &screenCallbacks, this);
|
||||
|
||||
// the terminal is doing most of the work
|
||||
keypad(false);
|
||||
|
||||
/// @todo block all signals, this thread does not handle any
|
||||
readPtyClientThread = std::thread(&NCursesPtyWindow::readFromPtyClientThreadMethod, this);
|
||||
}
|
||||
|
||||
NCursesPtyWindow::~NCursesPtyWindow()
|
||||
{
|
||||
close(fdPtyHost);
|
||||
close(fdPtyClient);
|
||||
vterm_free(pseudoTerminal);
|
||||
}
|
||||
|
||||
int NCursesPtyWindow::getFdPtyClient() const
|
||||
{
|
||||
return fdPtyClient;
|
||||
}
|
||||
|
||||
/// @todo maybe implement a function with a string buffer
|
||||
void NCursesPtyWindow::writeToClient(char character)
|
||||
{
|
||||
//__debug_log(std::string("written '") + character + "' to client");
|
||||
write(fdPtyHost, &character, sizeof(character));
|
||||
}
|
||||
|
||||
void NCursesPtyWindow::readFromPtyClientThreadMethod()
|
||||
{
|
||||
/// @todo in theory, there is no need for a timeout or select …
|
||||
/// file descriptor is blocking
|
||||
while(1)
|
||||
{
|
||||
readFromPtyClient();
|
||||
struct timeval timeout = { .tv_sec = 0, .tv_usec = 200000 };
|
||||
fd_set readFds;
|
||||
|
||||
FD_ZERO(&readFds);
|
||||
FD_SET(fdPtyHost, &readFds);
|
||||
if(select(fdPtyHost + 1, &readFds, NULL, NULL, &timeout) < 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void NCursesPtyWindow::readFromPtyClient()
|
||||
{
|
||||
size_t bytesRead = read(fdPtyHost, clientOutputBuffer, CLIENT_OUTPUT_BUFFER_SIZE);
|
||||
if(bytesRead)
|
||||
vterm_input_write(pseudoTerminal, clientOutputBuffer, bytesRead);
|
||||
__debug_log("read " + std::to_string(bytesRead) + " bytes from PTY client");
|
||||
/*string readable;
|
||||
for(char character : clientOutputBuffer)
|
||||
if(std::isprint((unsigned char) character))
|
||||
readable += character;
|
||||
else
|
||||
readable += "\\" + std::to_string((int) character) + "/";
|
||||
__debug_log("read: '" + readable + "'");*/
|
||||
}
|
||||
|
||||
VTermScreenCallbacks NCursesPtyWindow::screenCallbacks =
|
||||
{
|
||||
.damage = staticHandlerDamage,
|
||||
/* .moverect = staticHandlerMoveRect,
|
||||
.movecursor = staticHandlerMoveCursor,
|
||||
.settermprop = staticHandlerSetTermProp,
|
||||
.bell = staticHandlerBell,
|
||||
.resize = staticHandlerResize,
|
||||
.sb_pushline = staticHandlerPushLine,
|
||||
.sb_popline = staticHandlerPopLine,*/
|
||||
};
|
||||
|
||||
int NCursesPtyWindow::handlerDamage(VTermRect rect)
|
||||
{
|
||||
__debug_log("damage to rectangle ("
|
||||
+ std::to_string(rect.start_col) + ", "
|
||||
+ std::to_string(rect.start_row) + ", "
|
||||
+ std::to_string(rect.end_col) + ", "
|
||||
+ std::to_string(rect.end_row) + ") "
|
||||
+ "size: " + std::to_string((rect.start_col - rect.end_col) * (rect.start_row - rect.end_row)) );
|
||||
|
||||
int cursorX, cursorY;
|
||||
getyx(cursorY, cursorX);
|
||||
|
||||
for(int x = rect.start_col; x < rect.end_col; ++x)
|
||||
for(int y = rect.start_row; y < rect.end_row; ++y)
|
||||
copyPtyCellToNCursesWindow(x, y);
|
||||
|
||||
move(cursorY, cursorX);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int NCursesPtyWindow::handlerMoveRect(VTermRect dest, VTermRect src)
|
||||
{
|
||||
__debug_log("(unimplemented) move content in rectangle from ("
|
||||
+ std::to_string(src.start_col) + ", "
|
||||
+ std::to_string(src.start_row) + ", "
|
||||
+ std::to_string(src.end_col) + ", "
|
||||
+ std::to_string(src.end_row) + ") "
|
||||
+ "size: " + std::to_string((src.start_col - src.end_col) * (src.start_row - src.end_row))
|
||||
+ " to ("
|
||||
+ std::to_string(dest.start_col) + ", "
|
||||
+ std::to_string(dest.start_row) + ", "
|
||||
+ std::to_string(dest.end_col) + ", "
|
||||
+ std::to_string(dest.end_row) + ") "
|
||||
+ "size: " + std::to_string((dest.start_col - dest.end_col) * (dest.start_row - dest.end_row)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int NCursesPtyWindow::handlerMoveCursor(VTermPos pos, VTermPos oldpos, int visible)
|
||||
{
|
||||
__debug_log("cursor moved to ("
|
||||
+ std::to_string(pos.col) + ", "
|
||||
+ std::to_string(pos.row) + ") "
|
||||
+ "visible: " + (visible ? "true" : "false"));
|
||||
move(pos.row, pos.col);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int NCursesPtyWindow::handlerSetTermProp(VTermProp prop, VTermValue *val)
|
||||
{
|
||||
|
||||
__debug_log(std::string("(unimplemented) set terminal property: ")
|
||||
+ (prop == VTERM_PROP_CURSORVISIBLE
|
||||
? std::string("cursor visible = ")
|
||||
+ (val->boolean ? "true" : "false")
|
||||
: "")
|
||||
+ (prop == VTERM_PROP_CURSORBLINK
|
||||
? std::string("cursor blink = ")
|
||||
+ (val->boolean ? "true" : "false")
|
||||
: "")
|
||||
+ (prop == VTERM_PROP_ALTSCREEN
|
||||
? std::string("alt screen = ")
|
||||
+ (val->boolean ? "true" : "false")
|
||||
: "")
|
||||
+ (prop == VTERM_PROP_TITLE
|
||||
? std::string("title = ")
|
||||
+ val->string
|
||||
: "")
|
||||
+ (prop == VTERM_PROP_ICONNAME
|
||||
? std::string("icon name = ")
|
||||
+ val->string
|
||||
: "")
|
||||
+ (prop == VTERM_PROP_REVERSE
|
||||
? std::string("alt screen = ")
|
||||
+ (val->boolean ? "true" : "false")
|
||||
: "")
|
||||
+ (prop == VTERM_PROP_CURSORSHAPE
|
||||
? std::string("icon name = ")
|
||||
+ std::to_string(val->number)
|
||||
: "")
|
||||
+ (prop == VTERM_PROP_MOUSE
|
||||
? std::string("mouse = ")
|
||||
+ std::to_string(val->number)
|
||||
: "")
|
||||
+ (prop == VTERM_N_PROPS
|
||||
? "n props" : "")
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int NCursesPtyWindow::handlerBell()
|
||||
{
|
||||
beep();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int NCursesPtyWindow::handlerResize(int rows, int cols)
|
||||
{
|
||||
__debug_log("unimplemented handler called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int NCursesPtyWindow::handlerPushLine(int cols, const VTermScreenCell *cells)
|
||||
{
|
||||
__debug_log("(unimplemented) push line with " + std::to_string(cols) + " columns");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int NCursesPtyWindow::handlerPopLine(int cols, VTermScreenCell *cells)
|
||||
{
|
||||
__debug_log("unimplemented handler called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void NCursesPtyWindow::copyPtyCellToNCursesWindow(int x, int y)
|
||||
{
|
||||
VTermPos cellPosition = {y, x};
|
||||
VTermScreenCell cell;
|
||||
vterm_screen_get_cell(pseudoTerminalScreen, cellPosition, &cell);
|
||||
move(y, x);
|
||||
addch(cell.chars[0] ? cell.chars[0] : ' ' );
|
||||
/// @todo boy, is this expensive …
|
||||
refresh();
|
||||
}
|
||||
|
||||
int NCursesPtyWindow::staticHandlerDamage(VTermRect rect, void *user)
|
||||
{
|
||||
NCursesPtyWindow *instance = static_cast<NCursesPtyWindow *>(user);
|
||||
return instance->handlerDamage(rect);
|
||||
}
|
||||
|
||||
int NCursesPtyWindow::staticHandlerMoveRect(VTermRect dest, VTermRect src, void *user)
|
||||
{
|
||||
NCursesPtyWindow *instance = static_cast<NCursesPtyWindow *>(user);
|
||||
return instance->handlerMoveRect(dest, src);
|
||||
}
|
||||
|
||||
int NCursesPtyWindow::staticHandlerMoveCursor(VTermPos pos, VTermPos oldpos, int visible, void *user)
|
||||
{
|
||||
NCursesPtyWindow *instance = static_cast<NCursesPtyWindow *>(user);
|
||||
return instance->handlerMoveCursor(pos, oldpos, visible);
|
||||
}
|
||||
|
||||
int NCursesPtyWindow::staticHandlerSetTermProp(VTermProp prop, VTermValue *val, void *user)
|
||||
{
|
||||
NCursesPtyWindow *instance = static_cast<NCursesPtyWindow *>(user);
|
||||
return instance->handlerSetTermProp(prop, val);
|
||||
}
|
||||
|
||||
int NCursesPtyWindow::staticHandlerBell(void *user)
|
||||
{
|
||||
NCursesPtyWindow *instance = static_cast<NCursesPtyWindow *>(user);
|
||||
return instance->handlerBell();
|
||||
}
|
||||
|
||||
int NCursesPtyWindow::staticHandlerResize(int rows, int cols, void *user)
|
||||
{
|
||||
NCursesPtyWindow *instance = static_cast<NCursesPtyWindow *>(user);
|
||||
return instance->handlerResize(rows, cols);
|
||||
}
|
||||
|
||||
int NCursesPtyWindow::staticHandlerPushLine(int cols, const VTermScreenCell *cells, void *user)
|
||||
{
|
||||
NCursesPtyWindow *instance = static_cast<NCursesPtyWindow *>(user);
|
||||
return instance->handlerPushLine(cols, cells);
|
||||
}
|
||||
|
||||
int NCursesPtyWindow::staticHandlerPopLine(int cols, VTermScreenCell *cells, void *user)
|
||||
{
|
||||
NCursesPtyWindow *instance = static_cast<NCursesPtyWindow *>(user);
|
||||
return instance->handlerPopLine(cols, cells);
|
||||
}
|
||||
}
|
57
NCursesPtyWindow.hpp
Normal file
57
NCursesPtyWindow.hpp
Normal file
|
@ -0,0 +1,57 @@
|
|||
/**
|
||||
* @brief `ncurses` window displaying contents of a pseudo terminal
|
||||
* @author Christian Burger (christian@krikkel.de)
|
||||
*/
|
||||
|
||||
#include <cursesw.h>
|
||||
#include <pty.h>
|
||||
#include <vterm.h>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
namespace krikkel::NCursesPty
|
||||
{
|
||||
class NCursesPtyWindow : public NCursesWindow
|
||||
{
|
||||
public:
|
||||
NCursesPtyWindow(int lines, int columns, int y, int x);
|
||||
~NCursesPtyWindow();
|
||||
|
||||
int getFdPtyClient() const;
|
||||
void writeToClient(char character);
|
||||
|
||||
private:
|
||||
int fdPtyHost, fdPtyClient;
|
||||
struct termios terminalParameters;
|
||||
VTerm *pseudoTerminal;
|
||||
VTermScreen *pseudoTerminalScreen;
|
||||
static VTermScreenCallbacks screenCallbacks;
|
||||
/// @todo one line is at most 4096 chars long
|
||||
static const u_int CLIENT_OUTPUT_BUFFER_SIZE = 512;
|
||||
char clientOutputBuffer[CLIENT_OUTPUT_BUFFER_SIZE];
|
||||
|
||||
std::thread readPtyClientThread;
|
||||
void readFromPtyClientThreadMethod();
|
||||
void readFromPtyClient();
|
||||
|
||||
int handlerDamage(VTermRect rect);
|
||||
int handlerMoveRect(VTermRect dest, VTermRect src);
|
||||
int handlerMoveCursor(VTermPos pos, VTermPos oldpos, int visible);
|
||||
int handlerSetTermProp(VTermProp prop, VTermValue *val);
|
||||
int handlerBell();
|
||||
int handlerResize(int rows, int cols);
|
||||
int handlerPushLine(int cols, const VTermScreenCell *cells);
|
||||
int handlerPopLine(int cols, VTermScreenCell *cells);
|
||||
|
||||
void copyPtyCellToNCursesWindow(int x, int y);
|
||||
|
||||
static int staticHandlerDamage(VTermRect rect, void *user);
|
||||
static int staticHandlerMoveRect(VTermRect dest, VTermRect src, void *user);
|
||||
static int staticHandlerMoveCursor(VTermPos pos, VTermPos oldpos, int visible, void *user);
|
||||
static int staticHandlerSetTermProp(VTermProp prop, VTermValue *val, void *user);
|
||||
static int staticHandlerBell(void *user);
|
||||
static int staticHandlerResize(int rows, int cols, void *user);
|
||||
static int staticHandlerPushLine(int cols, const VTermScreenCell *cells, void *user);
|
||||
static int staticHandlerPopLine(int cols, VTermScreenCell *cells, void *user);
|
||||
};
|
||||
}
|
7
cmake/gsl.cmake
Normal file
7
cmake/gsl.cmake
Normal file
|
@ -0,0 +1,7 @@
|
|||
find_path(GSL_INCLUDE_DIR "gsl")
|
||||
if(GSL_INCLUDE_DIR STREQUAL "GSL_INCLUDE_DIR-NOTFOUND")
|
||||
message(SEND_ERROR "Microsoft GSL not found.")
|
||||
else()
|
||||
message(STATUS "Microsoft GSL found.")
|
||||
include_directories(SYSTEM ${GSL_INCLUDE_DIR})
|
||||
endif()
|
12
cmake/ncurses.cmake
Normal file
12
cmake/ncurses.cmake
Normal file
|
@ -0,0 +1,12 @@
|
|||
### ncurses
|
||||
set(CURSES_NEED_NCURSES TRUE)
|
||||
set(CURSES_NEED_WIDE TRUE)
|
||||
find_package(Curses 6.2 REQUIRED)
|
||||
include_directories(${CURSES_INCLUDE_DIRS})
|
||||
|
||||
# find C++ interface for ncurses with unicode support
|
||||
find_library(CURSES_CPP_WIDE_LIBRARY NAMES ncurses++w REQUIRED)
|
||||
if(NOT CURSES_CPP_WIDE_LIBRARY)
|
||||
message(FATAL_ERROR "C++ interface for ncurses (wide/unicode) not found.")
|
||||
endif()
|
||||
list(APPEND CURSES_LIBRARIES ${CURSES_CPP_WIDE_LIBRARY})
|
Loading…
Reference in a new issue