Compare commits

..

7 commits

8 changed files with 206 additions and 16 deletions

View file

@ -19,8 +19,10 @@ namespace krikkel::NCursesPtyWindow
int App::run() int App::run()
{ {
Window *ptyWindow = new Window( std::mutex writeMutex;
Root_Window->lines()
Window *ptyWindow = new Window(&writeMutex
, Root_Window->lines()
, Root_Window->cols() , Root_Window->cols()
, 0 , 0
, 0); , 0);

View file

@ -34,6 +34,15 @@ target_link_libraries(NCursesPtyWindow Threads::Threads)
add_executable(NCursesPtyApp main.cpp App.cpp) add_executable(NCursesPtyApp main.cpp App.cpp)
target_link_libraries(NCursesPtyApp ${CURSES_LIBRARIES} NCursesPtyWindow) target_link_libraries(NCursesPtyApp ${CURSES_LIBRARIES} NCursesPtyWindow)
### installation and packaging
set_target_properties(NCursesPtyWindow PROPERTIES PUBLIC_HEADER "Window.hpp;SingleUserInput.hpp")
install(TARGETS NCursesPtyWindow ARCHIVE PUBLIC_HEADER)
set(CPACK_PROJECT_NAME ${PROJECT_NAME}) set(CPACK_PROJECT_NAME ${PROJECT_NAME})
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION}) set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
set(CPACK_PACKAGE_CONTACT "Christian Burger <christian@krikkel.de>")
set(CPACK_PACKAGE_DESCRIPTION "Library for a pseudo terminal where the host end is a ncurses window")
set(CPACK_GENERATOR "DEB" "TGZ")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libncursesw6 (>=6.2), libvterm-dev (>= 0.1.2)")
include(CPack) include(CPack)

160
Debug.cpp
View file

@ -14,9 +14,14 @@
#include "Debug.hpp" #include "Debug.hpp"
#include <algorithm>
#include <cstring> #include <cstring>
#include <filesystem> #include <filesystem>
#include <cstdio> #include <cstdio>
#include <syscall.h>
#include <sys/ptrace.h>
#include <sys/stat.h>
#include <fcntl.h>
#define BUFFER_SIZE 128 #define BUFFER_SIZE 128
@ -29,6 +34,7 @@ namespace krikkel
using std::time; using std::time;
using std::localtime; using std::localtime;
using std::strftime; using std::strftime;
using std::min;
Debug::Debug() Debug::Debug()
{ {
@ -87,6 +93,160 @@ namespace krikkel
logFile.flush(); logFile.flush();
} }
std::string Debug::peekData(pid_t shellPid, void *data, size_t length)
{
std::string result;
for(int index = 0; index < length / sizeof(long); ++index)
{
long datum = ptrace(PTRACE_PEEKDATA, shellPid, ((char *) data) + index * sizeof(long));
if(length == -1)
{
size_t datumLength = strnlen((char *) &datum, sizeof(long));
result += string((char *) &datum, datumLength);
if(datumLength < sizeof(long))
break;
}
else
result += string((char *) &datum, sizeof(long));
}
return result;
}
std::string Debug::formatToAddress(unsigned long long address)
{
char addressString[17];
snprintf(addressString, 17, "%016llx", address);
return string("0x") + addressString;
}
void Debug::logPtraceSysCall(pid_t shellPid, uint16_t sysCallId, bool returnedFromSysCall, unsigned long long result, unsigned long long firstArgument, unsigned long long secondArgument, unsigned long long thirdArgument, unsigned long long fourthArgument, std::string fileName, int lineNo, std::string functionName)
{
const static uint8_t maxStringLength = 32;
string message = "syscall: ";
string sysCallName;
if(sysCallId < MAX_NUMBER_OF_SYSCALLS)
sysCallName = syscalls[sysCallId];
if(sysCallName.empty())
sysCallName = "syscall" + std::to_string(sysCallId);
switch(sysCallId)
{
case SYS_read:
if(returnedFromSysCall)
message += (result == -1 ? "-1" : std::to_string((size_t) result)) + " = ";
message += sysCallName
+ "(" + std::to_string((int) firstArgument)
+ ", \"" + (returnedFromSysCall ? __debug_make_bytes_printable(peekData(shellPid, (char *) secondArgument, (size_t) min<unsigned long long>(thirdArgument, maxStringLength))) : "")
+ (maxStringLength < thirdArgument || returnedFromSysCall == true ? "[…]" : "") + "\""
+ ", " + std::to_string((size_t) thirdArgument)
+ ")";
break;
case SYS_write:
if(returnedFromSysCall)
message += (result == -1 ? "-1" : std::to_string((size_t) result)) + " = ";
message += sysCallName
+ "(" + std::to_string((int) firstArgument)
+ ", \"" + __debug_make_bytes_printable(peekData(shellPid, (char *) secondArgument, (size_t) min<unsigned long long>(thirdArgument, maxStringLength)))
+ (maxStringLength < thirdArgument || returnedFromSysCall == true ? "[…]" : "") + "\""
+ ", " + std::to_string((size_t) thirdArgument)
+ ")";
break;
case SYS_close:
if(returnedFromSysCall)
message += std::to_string((int) result) + " = ";
message += sysCallName
+ "(" + std::to_string((int) firstArgument)
+ ")";
break;
case SYS_stat:
if(returnedFromSysCall)
message += std::to_string((int) result) + " = ";
message += sysCallName
+ "(\"" + peekData(shellPid, (char *) firstArgument, -1) + "\""
+ ", " + formatToAddress(secondArgument)
+ ")";
break;
case SYS_fstat:
if(returnedFromSysCall)
message += std::to_string((int) result) + " = ";
message += sysCallName
+ "(" + std::to_string((int) firstArgument)
+ ", " + formatToAddress(secondArgument)
+ ")";
break;
case SYS_openat:
if(returnedFromSysCall)
message += std::to_string((int) result) + " = ";
message += sysCallName
+ "(" + ((int) firstArgument == AT_FDCWD ? string("AT_FDCWD") : std::to_string((int) firstArgument))
+ ", \"" + __debug_make_bytes_printable(peekData(shellPid, (char *) secondArgument, -1)) + "\""
+ ", " + std::to_string((int) thirdArgument)
+ ")";
break;
case SYS_rt_sigprocmask:
if(returnedFromSysCall)
message += std::to_string((int) result) + " = ";
message += sysCallName
+ "(" + std::to_string((int) firstArgument)
+ ", " + formatToAddress(secondArgument)
+ ", " + formatToAddress(thirdArgument)
+ ", " + std::to_string((size_t) fourthArgument)
+ ")";
break;
case SYS_access:
if(returnedFromSysCall)
message += std::to_string((int) result) + " = ";
message += sysCallName
+ "(" + peekData(shellPid, (char *) firstArgument, -1)
+ ", " + std::to_string((int) secondArgument)
+ ")";
break;
case SYS_dup2:
if(returnedFromSysCall)
message += std::to_string((int) result) + " = ";
message += sysCallName
+ "(" + std::to_string((int) firstArgument)
+ ", " + std::to_string((int) secondArgument)
+ ")";
break;
case SYS_fcntl:
if(returnedFromSysCall)
message += std::to_string((int) result) + " = ";
message += sysCallName
+ "(" + std::to_string((int) firstArgument)
+ ", " + (secondArgument == 0 ? string("F_DUP_FD") : std::to_string((int) secondArgument))
+ ", …"
+ ")";
break;
/* case SYS_fstat:
struct stat statbuf;
message += "(" + std::to_string(firstArgument)
+ ", \"" + __debug_make_bytes_printable(peekData(shellPid, (char *) secondArgument, (size_t) min<unsigned long long>(thirdArgument, maxStringLength)))
+ (maxStringLength < thirdArgument ? "[…]" : "") + "\""
+ ", " + std::to_string((size_t) thirdArgument)
+ ")";
break;
*/ default:
if(returnedFromSysCall)
message += (result == -1 ? "-1" : std::to_string(result)) + " = ";
message += sysCallName
+ "(" + formatToAddress(firstArgument)
+ ", " + formatToAddress(secondArgument)
+ ", " + formatToAddress(thirdArgument)
+ ", " + formatToAddress(fourthArgument)
+ ")";
}
// source: https://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/
log(message, fileName, lineNo, functionName);
}
Debug *Debug::getInstance() Debug *Debug::getInstance()
{ {
static Debug *debug = new Debug(); static Debug *debug = new Debug();

View file

@ -26,17 +26,22 @@ namespace krikkel
Debug(); Debug();
std::string getTimestamp(); std::string getTimestamp();
std::string getUname(); std::string getUname();
std::string peekData(pid_t shellPid, void *data, size_t length);
std::string formatToAddress(unsigned long long address);
public: public:
void log(std::string message, std::string fileName, int lineNo void log(std::string message, std::string fileName, int lineNo
, std::string functionName); , std::string functionName);
void logPtraceSysCall(pid_t shellPid, uint16_t sysCallId, bool returnedFromSysCall, unsigned long long result, unsigned long long firstArgument, unsigned long long secondArgument, unsigned long long thirdArgument, unsigned long long fourthArgument, std::string fileName, int lineNo, std::string functionName);
static Debug *getInstance(); static Debug *getInstance();
}; };
} }
#define __debug_log(message) krikkel::Debug::getInstance()\ #define __debug_log(message) \
->log(message, __FILE__, __LINE__, __func__) krikkel::Debug::getInstance()->log(message, __FILE__, __LINE__, __func__)
#define __debug_log_ptrace_syscall(shellPid, sysCallId, returnedFromSysCall, result, firstArgument, secondArgument, thirdArgument, fourthArgument) \
krikkel::Debug::getInstance()->logPtraceSysCall(shellPid, sysCallId, returnedFromSysCall, result, firstArgument, secondArgument, thirdArgument, fourthArgument, __FILE__, __LINE__, __func__);
/// @todo need an explanation on why and how to use this /// @todo need an explanation on why and how to use this
/// this is the complete opposite of self-explanatory /// this is the complete opposite of self-explanatory
@ -104,7 +109,7 @@ namespace krikkel
if(std::isprint(character, loc) && character != '<') \ if(std::isprint(character, loc) && character != '<') \
result += character; \ result += character; \
else \ else \
result += '<' + std::to_string(character) + '>'; \ result += '<' + std::to_string((unsigned char) character) + '>'; \
} \ } \
return result; \ return result; \
})(__bytes) })(__bytes)

View file

@ -1,3 +1,8 @@
/**
* @brief stores a single user input (printable and function keys)
* @author Christian Burger (christian@krikkel.de)
*/
#ifndef F0E30ED4_3883_40D6_A6EE_08BA4DF9E92E #ifndef F0E30ED4_3883_40D6_A6EE_08BA4DF9E92E
#define F0E30ED4_3883_40D6_A6EE_08BA4DF9E92E #define F0E30ED4_3883_40D6_A6EE_08BA4DF9E92E

View file

@ -18,8 +18,8 @@ namespace krikkel::NCursesPtyWindow
using gsl::narrow; using gsl::narrow;
using std::lock_guard; using std::lock_guard;
Window::Window(int lines, int columns, int y, int x) Window::Window(std::mutex *writeToNCursesMutex, int lines, int columns, int y, int x)
: NCursesWindow(lines, columns, y, x) : writeToNCursesMutex(writeToNCursesMutex), NCursesWindow(lines, columns, y, x)
{ {
// to get the original terminal we need to shutdown ncurses for a moment // to get the original terminal we need to shutdown ncurses for a moment
/// @todo maybe try `reset_prog_mode()` and `reset_shell_mode()` instead /// @todo maybe try `reset_prog_mode()` and `reset_shell_mode()` instead
@ -245,11 +245,14 @@ namespace krikkel::NCursesPtyWindow
else else
character = *vTermCell.chars; character = *vTermCell.chars;
{
lock_guard nCursesLock(*writeToNCursesMutex);
setcchar(&converted, &character, formatting, colorPair, NULL); setcchar(&converted, &character, formatting, colorPair, NULL);
move(cellPosition.row, cellPosition.col); move(cellPosition.row, cellPosition.col);
chgat(1, formatting, colorPair, NULL); chgat(1, formatting, colorPair, NULL);
add_wch(&converted); add_wch(&converted);
} }
}
int Window::add_wch(const cchar_t *character) int Window::add_wch(const cchar_t *character)
{ {
@ -263,6 +266,7 @@ namespace krikkel::NCursesPtyWindow
int Window::refresh() int Window::refresh()
{ {
lock_guard nCursesLock(*writeToNCursesMutex);
move(cursorY, cursorX); move(cursorY, cursorX);
return NCursesWindow::refresh(); return NCursesWindow::refresh();
} }
@ -271,7 +275,8 @@ namespace krikkel::NCursesPtyWindow
/// resizing? /// resizing?
int Window::wresize(int rows, int cols) int Window::wresize(int rows, int cols)
{ {
std::lock_guard writeLock(writeToPseudoTerminalMutex); lock_guard nCursesLock(*writeToNCursesMutex);
lock_guard writeLock(writeToPseudoTerminalMutex);
winsize windowSize = winsize windowSize =
{ {
.ws_row = narrow<unsigned short>(rows) .ws_row = narrow<unsigned short>(rows)

View file

@ -3,6 +3,9 @@
* @author Christian Burger (christian@krikkel.de) * @author Christian Burger (christian@krikkel.de)
*/ */
#ifndef __WINDOW_H__
#define __WINDOW_H__
#include "SingleUserInput.hpp" #include "SingleUserInput.hpp"
#include <ncursesw/cursesw.h> #include <ncursesw/cursesw.h>
@ -29,7 +32,7 @@ namespace krikkel::NCursesPtyWindow
class Window : public NCursesWindow class Window : public NCursesWindow
{ {
public: public:
Window(int lines, int columns, int y, int x); Window(std::mutex *writeToNCursesMutex, int lines, int columns, int y, int x);
~Window(); ~Window();
int getFdPtyClient() const; int getFdPtyClient() const;
@ -49,6 +52,7 @@ namespace krikkel::NCursesPtyWindow
struct termios terminalParameters; struct termios terminalParameters;
VTerm *pseudoTerminal; VTerm *pseudoTerminal;
std::mutex writeToPseudoTerminalMutex; std::mutex writeToPseudoTerminalMutex;
std::mutex *writeToNCursesMutex;
VTermScreen *pseudoTerminalScreen; VTermScreen *pseudoTerminalScreen;
static VTermScreenCallbacks screenCallbacks; static VTermScreenCallbacks screenCallbacks;
/// @todo one line is at most 4096 chars long /// @todo one line is at most 4096 chars long
@ -84,3 +88,4 @@ namespace krikkel::NCursesPtyWindow
static void staticHandlerOutput(const char *s, size_t len, void *user); static void staticHandlerOutput(const char *s, size_t len, void *user);
}; };
} }
#endif // __WINDOW_H__

View file

@ -1,10 +1,9 @@
# @author Christian Burger <christian@krikkel.de> # @author Christian Burger <christian@krikkel.de>
### ncurses
set(CURSES_NEED_NCURSES TRUE) set(CURSES_NEED_NCURSES TRUE)
set(CURSES_NEED_WIDE TRUE) set(CURSES_NEED_WIDE TRUE)
find_package(Curses 6.2 REQUIRED) find_package(Curses 6.2 REQUIRED)
include_directories(${CURSES_INCLUDE_DIRS}) include_directories(SYSTEM ${CURSES_INCLUDE_DIRS})
# find C++ interface for ncurses with unicode support # find C++ interface for ncurses with unicode support
find_library(CURSES_CPP_WIDE_LIBRARY NAMES ncurses++w REQUIRED) find_library(CURSES_CPP_WIDE_LIBRARY NAMES ncurses++w REQUIRED)