updated codestyle
Prepare Build and Testing executable binary / Prepare-Build-Testing-With-Make-Without-sqlite3 (push) Successful in 6s
Prepare Build and Testing executable binary / Prepare-Build-Testing-With-CMake-Without-sqlite3 (push) Successful in 9s
Prepare Build and Testing executable binary / Prepare-Build-Testing-With-Meson-Without-sqlite3 (push) Successful in 7s

This commit is contained in:
wt committed 2025-11-18 11:48:46 +07:00
1 parent 193779bd13
commit 36aa1bea2a
12 files changed
+18657 -4237

No files matched your search

+1 -1
View File
@@ -8,7 +8,7 @@ OBJS = $(SRCS:.c=.o)
TARGET = wterm TARGET = wterm
BUILD_DIR=./build BUILD_DIR=./build
CROSS_COMPILE = CROSS_COMPILE =
LIBS = -lpthread LIBS = -lpthread
# -lcrypto -lssl # -lcrypto -lssl
USE_SQLITE3 ?= 0 USE_SQLITE3 ?= 0
WERROR ?= 0 WERROR ?= 0
+2244 -2570
View File
File diff suppressed because it is too large. Load diff
+218 -129
View File
@@ -24,23 +24,27 @@
#define cJSON__h #define cJSON__h
#ifdef __cplusplus #ifdef __cplusplus
extern "C" extern "C" {
{
#endif #endif
#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) #if !defined(__WINDOWS__) && \
(defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))
#define __WINDOWS__ #define __WINDOWS__
#endif #endif
#ifdef __WINDOWS__ #ifdef __WINDOWS__
/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options: /* When compiling for windows, we specify a specific calling convention to avoid
issues where we are being called from a project with a different default calling
convention. For windows you have 3 define options:
CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever
CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default) dllexport symbols CJSON_EXPORT_SYMBOLS - Define this on library build when you
CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol want to dllexport symbols (default) CJSON_IMPORT_SYMBOLS - Define this if you
want to dllimport symbol
For *nix builds that support visibility attribute, you can define similar behavior by For *nix builds that support visibility attribute, you can define similar
behavior by
setting default visibility to hidden by adding setting default visibility to hidden by adding
-fvisibility=hidden (for gcc) -fvisibility=hidden (for gcc)
@@ -48,31 +52,35 @@ or
-xldscope=hidden (for sun cc) -xldscope=hidden (for sun cc)
to CFLAGS to CFLAGS
then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way
CJSON_EXPORT_SYMBOLS does
*/ */
#define CJSON_CDECL __cdecl #define CJSON_CDECL __cdecl
#define CJSON_STDCALL __stdcall #define CJSON_STDCALL __stdcall
/* export symbols by default, this is necessary for copy pasting the C and header file */ /* export symbols by default, this is necessary for copy pasting the C and
#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS) * header file */
#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && \
!defined(CJSON_EXPORT_SYMBOLS)
#define CJSON_EXPORT_SYMBOLS #define CJSON_EXPORT_SYMBOLS
#endif #endif
#if defined(CJSON_HIDE_SYMBOLS) #if defined(CJSON_HIDE_SYMBOLS)
#define CJSON_PUBLIC(type) type CJSON_STDCALL #define CJSON_PUBLIC(type) type CJSON_STDCALL
#elif defined(CJSON_EXPORT_SYMBOLS) #elif defined(CJSON_EXPORT_SYMBOLS)
#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL #define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL
#elif defined(CJSON_IMPORT_SYMBOLS) #elif defined(CJSON_IMPORT_SYMBOLS)
#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL #define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL
#endif #endif
#else /* !__WINDOWS__ */ #else /* !__WINDOWS__ */
#define CJSON_CDECL #define CJSON_CDECL
#define CJSON_STDCALL #define CJSON_STDCALL
#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY) #if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined(__SUNPRO_C)) && \
#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type defined(CJSON_API_VISIBILITY)
#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type
#else #else
#define CJSON_PUBLIC(type) type #define CJSON_PUBLIC(type) type
#endif #endif
@@ -87,115 +95,146 @@ then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJ
/* cJSON Types: */ /* cJSON Types: */
#define cJSON_Invalid (0) #define cJSON_Invalid (0)
#define cJSON_False (1 << 0) #define cJSON_False (1 << 0)
#define cJSON_True (1 << 1) #define cJSON_True (1 << 1)
#define cJSON_NULL (1 << 2) #define cJSON_NULL (1 << 2)
#define cJSON_Number (1 << 3) #define cJSON_Number (1 << 3)
#define cJSON_String (1 << 4) #define cJSON_String (1 << 4)
#define cJSON_Array (1 << 5) #define cJSON_Array (1 << 5)
#define cJSON_Object (1 << 6) #define cJSON_Object (1 << 6)
#define cJSON_Raw (1 << 7) /* raw json */ #define cJSON_Raw (1 << 7) /* raw json */
#define cJSON_IsReference 256 #define cJSON_IsReference 256
#define cJSON_StringIsConst 512 #define cJSON_StringIsConst 512
/* The cJSON structure: */ /* The cJSON structure: */
typedef struct cJSON typedef struct cJSON {
{ /* next/prev allow you to walk array/object chains. Alternatively, use
/* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ * GetArraySize/GetArrayItem/GetObjectItem */
struct cJSON *next; struct cJSON *next;
struct cJSON *prev; struct cJSON *prev;
/* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ /* An array or object item will have a child pointer pointing to a chain of
struct cJSON *child; * the items in the array/object. */
struct cJSON *child;
/* The type of the item, as above. */ /* The type of the item, as above. */
int type; int type;
/* The item's string, if type==cJSON_String and type == cJSON_Raw */ /* The item's string, if type==cJSON_String and type == cJSON_Raw */
char *valuestring; char *valuestring;
/* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
int valueint; int valueint;
/* The item's number, if type==cJSON_Number */ /* The item's number, if type==cJSON_Number */
double valuedouble; double valuedouble;
/* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ /* The item's name string, if this item is the child of, or is in the list of
char *string; * subitems of an object. */
char *string;
} cJSON; } cJSON;
typedef struct cJSON_Hooks typedef struct cJSON_Hooks {
{ /* malloc/free are CDECL on Windows regardless of the default calling
/* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */ * convention of the compiler, so ensure the hooks allow passing those
void *(CJSON_CDECL *malloc_fn)(size_t sz); * functions directly. */
void (CJSON_CDECL *free_fn)(void *ptr); void *(CJSON_CDECL *malloc_fn)(size_t sz);
void(CJSON_CDECL *free_fn)(void *ptr);
} cJSON_Hooks; } cJSON_Hooks;
typedef int cJSON_bool; typedef int cJSON_bool;
/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them. /* Limits how deeply nested arrays/objects can be before cJSON rejects to parse
* This is to prevent stack overflows. */ * them. This is to prevent stack overflows. */
#ifndef CJSON_NESTING_LIMIT #ifndef CJSON_NESTING_LIMIT
#define CJSON_NESTING_LIMIT 1000 #define CJSON_NESTING_LIMIT 1000
#endif #endif
/* Limits the length of circular references can be before cJSON rejects to parse them. /* Limits the length of circular references can be before cJSON rejects to parse
* This is to prevent stack overflows. */ * them. This is to prevent stack overflows. */
#ifndef CJSON_CIRCULAR_LIMIT #ifndef CJSON_CIRCULAR_LIMIT
#define CJSON_CIRCULAR_LIMIT 10000 #define CJSON_CIRCULAR_LIMIT 10000
#endif #endif
/* returns the version of cJSON as a string */ /* returns the version of cJSON as a string */
CJSON_PUBLIC(const char*) cJSON_Version(void); CJSON_PUBLIC(const char *) cJSON_Version(void);
/* Supply malloc, realloc and free functions to cJSON */ /* Supply malloc, realloc and free functions to cJSON */
CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks *hooks);
/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */ /* Memory Management: the caller is always responsible to free the results from
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ * all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib
* free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is
* cJSON_PrintPreallocated, where the caller has full responsibility of the
* buffer. */
/* Supply a block of JSON, and this returns a cJSON object you can interrogate.
*/
CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length); CJSON_PUBLIC(cJSON *)
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ cJSON_ParseWithLength(const char *value, size_t buffer_length);
/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */ /* ParseWithOpts allows you to require (and check) that the JSON is null
CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); * terminated, and to retrieve the pointer to the final byte parsed. */
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated); /* If you supply a ptr in return_parse_end and parsing fails, then
* return_parse_end will contain a pointer to the error so will match
* cJSON_GetErrorPtr(). */
CJSON_PUBLIC(cJSON *)
cJSON_ParseWithOpts(const char *value, const char **return_parse_end,
cJSON_bool require_null_terminated);
CJSON_PUBLIC(cJSON *)
cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length,
const char **return_parse_end,
cJSON_bool require_null_terminated);
/* Render a cJSON entity to text for transfer/storage. */ /* Render a cJSON entity to text for transfer/storage. */
CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
/* Render a cJSON entity to text for transfer/storage without any formatting. */ /* Render a cJSON entity to text for transfer/storage without any formatting. */
CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item); CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ /* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess
CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt); * at the final size. guessing well reduces reallocation. fmt=0 gives
/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */ * unformatted, =1 gives formatted */
/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */ CJSON_PUBLIC(char *)
CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
/* Render a cJSON entity to text using a buffer already allocated in memory with
* given length. Returns 1 on success and 0 on failure. */
/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will
* use, so to be safe allocate 5 bytes more than you actually need */
CJSON_PUBLIC(cJSON_bool)
cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length,
const cJSON_bool format);
/* Delete a cJSON entity and all subentities. */ /* Delete a cJSON entity and all subentities. */
CJSON_PUBLIC(void) cJSON_Delete(cJSON *item); CJSON_PUBLIC(void) cJSON_Delete(cJSON *item);
/* Returns the number of items in an array (or object). */ /* Returns the number of items in an array (or object). */
CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */ /* Retrieve item number "index" from array "array". Returns NULL if
* unsuccessful. */
CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
/* Get item "string" from object. Case insensitive. */ /* Get item "string" from object. Case insensitive. */
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string); CJSON_PUBLIC(cJSON *)
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string); cJSON_GetObjectItem(const cJSON *const object, const char *const string);
CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string); CJSON_PUBLIC(cJSON *)
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ cJSON_GetObjectItemCaseSensitive(const cJSON *const object,
const char *const string);
CJSON_PUBLIC(cJSON_bool)
cJSON_HasObjectItem(const cJSON *object, const char *string);
/* For analysing failed parses. This returns a pointer to the parse error.
* You'll probably need to look a few chars back to make sense of it. Defined
* when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
/* Check item type and return its value */ /* Check item type and return its value */
CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item); CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON *const item);
CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item); CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON *const item);
/* These functions check the type of an item */ /* These functions check the type of an item */
CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON *const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON *const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON *const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON *const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON *const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON *const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON *const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON *const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON *const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON *const item);
/* These calls create a cJSON item of the appropriate type. */ /* These calls create a cJSON item of the appropriate type. */
CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void); CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
@@ -218,84 +257,134 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child);
CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child); CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child);
/* These utilities create an Array of count items. /* These utilities create an Array of count items.
* The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/ * The parameter count cannot be greater than the number of elements in the
* number array, otherwise array access will be out of bounds.*/
CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count); CJSON_PUBLIC(cJSON *)
cJSON_CreateStringArray(const char *const *strings, int count);
/* Append item to the specified array/object. */ /* Append item to the specified array/object. */
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item); CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item);
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); CJSON_PUBLIC(cJSON_bool)
/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
* WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before /* Use this when string is definitely const (i.e. a literal, or as good as), and
* writing to `item->string` */ * will definitely survive the cJSON object. WARNING: When this function was
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); * used, make sure to always check that (item->type & cJSON_StringIsConst) is
/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ * zero before writing to `item->string` */
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); CJSON_PUBLIC(cJSON_bool)
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
/* Append reference to item to the specified array/object. Use this when you
* want to add an existing cJSON to a new cJSON, but don't want to corrupt your
* existing cJSON. */
CJSON_PUBLIC(cJSON_bool)
cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
CJSON_PUBLIC(cJSON_bool)
cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
/* Remove/Detach items from Arrays/Objects. */ /* Remove/Detach items from Arrays/Objects. */
CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); CJSON_PUBLIC(cJSON *)
cJSON_DetachItemViaPointer(cJSON *parent, cJSON *const item);
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string); CJSON_PUBLIC(cJSON *)
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string); cJSON_DetachItemFromObject(cJSON *object, const char *string);
CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string); CJSON_PUBLIC(cJSON *)
CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
CJSON_PUBLIC(void)
cJSON_DeleteItemFromObject(cJSON *object, const char *string);
CJSON_PUBLIC(void)
cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string);
/* Update array items. */ /* Update array items. */
CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ CJSON_PUBLIC(cJSON_bool)
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement); cJSON_InsertItemInArray(
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); cJSON *array, int which,
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); cJSON *newitem); /* Shifts pre-existing items to the right. */
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); CJSON_PUBLIC(cJSON_bool)
cJSON_ReplaceItemViaPointer(cJSON *const parent, cJSON *const item,
cJSON *replacement);
CJSON_PUBLIC(cJSON_bool)
cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
CJSON_PUBLIC(cJSON_bool)
cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem);
CJSON_PUBLIC(cJSON_bool)
cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string,
cJSON *newitem);
/* Duplicate a cJSON item */ /* Duplicate a cJSON item */
CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse);
/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will /* Duplicate will create a new, identical cJSON item to the one you pass, in new
* need to be released. With recurse!=0, it will duplicate any children connected to the item. * memory that will need to be released. With recurse!=0, it will duplicate any
* The item->next and ->prev pointers are always zero on return from Duplicate. */ * children connected to the item. The item->next and ->prev pointers are always
/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. * zero on return from Duplicate. */
* case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ /* Recursively compare two cJSON items for equality. If either a or b is NULL or
CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive); * invalid, they will be considered unequal. case_sensitive determines if object
* keys are treated case sensitive (1) or case insensitive (0) */
CJSON_PUBLIC(cJSON_bool)
cJSON_Compare(const cJSON *const a, const cJSON *const b,
const cJSON_bool case_sensitive);
/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings. /* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from
* The input pointer json cannot point to a read-only address area, such as a string constant, * strings. The input pointer json cannot point to a read-only address area,
* but should point to a readable and writable address area. */ * such as a string constant, but should point to a readable and writable
* address area. */
CJSON_PUBLIC(void) cJSON_Minify(char *json); CJSON_PUBLIC(void) cJSON_Minify(char *json);
/* Helper functions for creating and adding items to an object at the same time. /* Helper functions for creating and adding items to an object at the same time.
* They return the added item or NULL on failure. */ * They return the added item or NULL on failure. */
CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name); CJSON_PUBLIC(cJSON *)
CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name); cJSON_AddNullToObject(cJSON *const object, const char *const name);
CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name); CJSON_PUBLIC(cJSON *)
CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean); cJSON_AddTrueToObject(cJSON *const object, const char *const name);
CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number); CJSON_PUBLIC(cJSON *)
CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string); cJSON_AddFalseToObject(cJSON *const object, const char *const name);
CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw); CJSON_PUBLIC(cJSON *)
CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name); cJSON_AddBoolToObject(cJSON *const object, const char *const name,
CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name); const cJSON_bool boolean);
CJSON_PUBLIC(cJSON *)
cJSON_AddNumberToObject(cJSON *const object, const char *const name,
const double number);
CJSON_PUBLIC(cJSON *)
cJSON_AddStringToObject(cJSON *const object, const char *const name,
const char *const string);
CJSON_PUBLIC(cJSON *)
cJSON_AddRawToObject(cJSON *const object, const char *const name,
const char *const raw);
CJSON_PUBLIC(cJSON *)
cJSON_AddObjectToObject(cJSON *const object, const char *const name);
CJSON_PUBLIC(cJSON *)
cJSON_AddArrayToObject(cJSON *const object, const char *const name);
/* When assigning an integer value, it needs to be propagated to valuedouble too. */ /* When assigning an integer value, it needs to be propagated to valuedouble
#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) * too. */
#define cJSON_SetIntValue(object, number) \
((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
/* helper for the cJSON_SetNumberValue macro */ /* helper for the cJSON_SetNumberValue macro */
CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) #define cJSON_SetNumberValue(object, number) \
/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */ ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring); /* Change the valuestring of a cJSON_String object, only takes effect when type
* of object is cJSON_String */
CJSON_PUBLIC(char *)
cJSON_SetValuestring(cJSON *object, const char *valuestring);
/* If the object is not a boolean type this does nothing and returns cJSON_Invalid else it returns the new type*/ /* If the object is not a boolean type this does nothing and returns
#define cJSON_SetBoolValue(object, boolValue) ( \ * cJSON_Invalid else it returns the new type*/
(object != NULL && ((object)->type & (cJSON_False|cJSON_True))) ? \ #define cJSON_SetBoolValue(object, boolValue) \
(object)->type=((object)->type &(~(cJSON_False|cJSON_True)))|((boolValue)?cJSON_True:cJSON_False) : \ ((object != NULL && ((object)->type & (cJSON_False | cJSON_True))) \
cJSON_Invalid\ ? (object)->type = ((object)->type & (~(cJSON_False | cJSON_True))) | \
) ((boolValue) ? cJSON_True : cJSON_False) \
: cJSON_Invalid)
/* Macro for iterating over an array or object */ /* Macro for iterating over an array or object */
#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) #define cJSON_ArrayForEach(element, array) \
for (element = (array != NULL) ? (array)->child : NULL; element != NULL; \
element = element->next)
/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ /* malloc/free objects using the malloc/free functions that have been set with
* cJSON_InitHooks */
CJSON_PUBLIC(void *) cJSON_malloc(size_t size); CJSON_PUBLIC(void *) cJSON_malloc(size_t size);
CJSON_PUBLIC(void) cJSON_free(void *object); CJSON_PUBLIC(void) cJSON_free(void *object);
+268 -251
View File
@@ -1,13 +1,13 @@
#include "terminal.h"
#include <cJSON.h>
#include <errno.h>
#include <mongoose.h>
#include <signal.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <errno.h> #include <sys/wait.h>
#include <mongoose.h>
#include <cJSON.h>
#include "terminal.h"
#define HTTP_PORT "8888" #define HTTP_PORT "8888"
#define STATIC_DIR "./static/" #define STATIC_DIR "./static/"
@@ -16,284 +16,301 @@ static const char *s_http_addr = "http://0.0.0.0:8888";
static struct mg_mgr mgr; static struct mg_mgr mgr;
struct client_data { struct client_data {
struct terminal_session term; struct terminal_session term;
bool terminal_ready; bool terminal_ready;
}; };
// Функция для логирования запросов // Функция для логирования запросов
static void log_request(struct mg_connection *c, struct mg_http_message *hm) { static void log_request(struct mg_connection *c, struct mg_http_message *hm) {
printf("HTTP Request: %.*s %.*s\n", (int)hm->method.len, hm->method.buf, printf("HTTP Request: %.*s %.*s\n", (int)hm->method.len, hm->method.buf,
(int)hm->uri.len, hm->uri.buf); (int)hm->uri.len, hm->uri.buf);
} }
// Проверяем, жив ли дочерний процесс // Проверяем, жив ли дочерний процесс
static bool is_process_alive(pid_t pid) { static bool is_process_alive(pid_t pid) {
if (pid <= 0) return false; if (pid <= 0)
return false;
int status;
pid_t result = waitpid(pid, &status, WNOHANG); int status;
if (result == 0) { pid_t result = waitpid(pid, &status, WNOHANG);
// Процесс все еще работает if (result == 0) {
return true; // Процесс все еще работает
} else if (result == -1) { return true;
// Ошибка } else if (result == -1) {
if (errno == ECHILD) { // Ошибка
// Процесс не существует if (errno == ECHILD) {
return false; // Процесс не существует
} return false;
perror("waitpid");
return false;
} else {
// Процесс завершился
return false;
} }
perror("waitpid");
return false;
} else {
// Процесс завершился
return false;
}
} }
// Перезапускаем терминальную сессию // Перезапускаем терминальную сессию
static void restart_terminal_session(struct mg_connection *c, struct client_data *cd) { static void restart_terminal_session(struct mg_connection *c,
if (!cd) return; struct client_data *cd) {
if (!cd)
printf("Restarting terminal session...\n"); return;
// Уничтожаем старую сессию printf("Restarting terminal session...\n");
if (cd->terminal_ready) {
destroy_terminal_session(&cd->term); // Уничтожаем старую сессию
} if (cd->terminal_ready) {
destroy_terminal_session(&cd->term);
// Создаем новую сессию }
const char *shell = getenv("SHELL");
if (!shell) shell = "/bin/bash"; // Создаем новую сессию
const char *shell = getenv("SHELL");
if (create_terminal_session(&cd->term, shell) == 0) { if (!shell)
cd->terminal_ready = true; shell = "/bin/bash";
mg_ws_send(c, "\r\n*** Terminal restarted ***\r\n", 29, WEBSOCKET_OP_TEXT);
} else { if (create_terminal_session(&cd->term, shell) == 0) {
mg_ws_send(c, "\r\n*** Failed to restart terminal ***\r\n", 37, WEBSOCKET_OP_TEXT); cd->terminal_ready = true;
cd->terminal_ready = false; mg_ws_send(c, "\r\n*** Terminal restarted ***\r\n", 29, WEBSOCKET_OP_TEXT);
} } else {
mg_ws_send(c, "\r\n*** Failed to restart terminal ***\r\n", 37,
WEBSOCKET_OP_TEXT);
cd->terminal_ready = false;
}
} }
// Обработчик WebSocket сообщений // Обработчик WebSocket сообщений
static void handle_websocket_message(struct mg_connection *c, struct mg_ws_message *wm, struct client_data *cd) { static void handle_websocket_message(struct mg_connection *c,
if (!cd) return; struct mg_ws_message *wm,
struct client_data *cd) {
if (wm->data.len == 0) { if (!cd)
return; return;
if (wm->data.len == 0) {
return;
}
// Парсим JSON для проверки resize сообщения
cJSON *json = cJSON_ParseWithLength(wm->data.buf, wm->data.len);
if (json != NULL) {
cJSON *cols = cJSON_GetObjectItemCaseSensitive(json, "cols");
cJSON *rows = cJSON_GetObjectItemCaseSensitive(json, "rows");
if (cJSON_IsNumber(cols) && cJSON_IsNumber(rows)) {
printf("Resizing terminal to %dx%d\n", rows->valueint, cols->valueint);
resize_terminal(&cd->term, rows->valueint, cols->valueint);
cJSON_Delete(json);
return;
} }
cJSON_Delete(json);
// Парсим JSON для проверки resize сообщения }
cJSON *json = cJSON_ParseWithLength(wm->data.buf, wm->data.len);
if (json != NULL) { // Проверяем, не команда ли это exit
cJSON *cols = cJSON_GetObjectItemCaseSensitive(json, "cols"); if (wm->data.len >= 4 && strncmp(wm->data.buf, "exit", 4) == 0) {
cJSON *rows = cJSON_GetObjectItemCaseSensitive(json, "rows"); printf("Exit command detected, restarting terminal session\n");
if (cJSON_IsNumber(cols) && cJSON_IsNumber(rows)) {
printf("Resizing terminal to %dx%d\n", rows->valueint, cols->valueint);
resize_terminal(&cd->term, rows->valueint, cols->valueint);
cJSON_Delete(json);
return;
}
cJSON_Delete(json);
}
// Проверяем, не команда ли это exit
if (wm->data.len >= 4 && strncmp(wm->data.buf, "exit", 4) == 0) {
printf("Exit command detected, restarting terminal session\n");
write_to_terminal(&cd->term, wm->data.buf, wm->data.len);
// Даем команде выполниться, затем перезапускаем
usleep(100);
restart_terminal_session(c, cd);
return;
}
// Обычный ввод от пользователя
write_to_terminal(&cd->term, wm->data.buf, wm->data.len); write_to_terminal(&cd->term, wm->data.buf, wm->data.len);
// Даем команде выполниться, затем перезапускаем
usleep(100);
restart_terminal_session(c, cd);
return;
}
// Обычный ввод от пользователя
write_to_terminal(&cd->term, wm->data.buf, wm->data.len);
} }
// Обработчик HTTP запросов // Обработчик HTTP запросов
static void fn(struct mg_connection *c, int ev, void *ev_data) { static void fn(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_HTTP_MSG) { if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *)ev_data; struct mg_http_message *hm = (struct mg_http_message *)ev_data;
log_request(c, hm); log_request(c, hm);
if (mg_match(hm->uri, mg_str("/ws"), nullptr)) { if (mg_match(hm->uri, mg_str("/ws"), nullptr)) {
// Проверяем, нет ли уже активного соединения // Проверяем, нет ли уже активного соединения
if (c->fn_data != NULL) { if (c->fn_data != NULL) {
printf("WebSocket connection already exists for this connection, cleaning up...\n"); printf("WebSocket connection already exists for this connection, "
struct client_data *old_cd = (struct client_data *)c->fn_data; "cleaning up...\n");
destroy_terminal_session(&old_cd->term); struct client_data *old_cd = (struct client_data *)c->fn_data;
free(old_cd); destroy_terminal_session(&old_cd->term);
c->fn_data = NULL; free(old_cd);
} c->fn_data = NULL;
}
// Upgrade до WebSocket
struct client_data *cd = calloc(1, sizeof(struct client_data)); // Upgrade до WebSocket
if (!cd) { struct client_data *cd = calloc(1, sizeof(struct client_data));
mg_http_reply(c, 500, "Content-Type: text/plain\r\n", "Memory allocation failed"); if (!cd) {
return; mg_http_reply(c, 500, "Content-Type: text/plain\r\n",
} "Memory allocation failed");
return;
// Создаем терминальную сессию }
const char *shell = getenv("SHELL");
if (!shell) shell = "/bin/bash"; // Создаем терминальную сессию
const char *shell = getenv("SHELL");
printf("Creating new terminal session with shell: %s\n", shell); if (!shell)
if (create_terminal_session(&cd->term, shell) != 0) { shell = "/bin/bash";
mg_http_reply(c, 500, "Content-Type: text/plain\r\n", "Failed to create terminal session");
free(cd); printf("Creating new terminal session with shell: %s\n", shell);
return; if (create_terminal_session(&cd->term, shell) != 0) {
} mg_http_reply(c, 500, "Content-Type: text/plain\r\n",
"Failed to create terminal session");
cd->terminal_ready = true; free(cd);
c->fn_data = cd; return;
}
// Upgrade to WebSocket
struct mg_str *ws_proto = mg_http_get_header(hm, "Sec-WebSocket-Protocol"); cd->terminal_ready = true;
if (ws_proto != NULL) { c->fn_data = cd;
mg_ws_upgrade(c, hm, ws_proto->buf);
} else { // Upgrade to WebSocket
mg_ws_upgrade(c, hm, NULL); struct mg_str *ws_proto =
} mg_http_get_header(hm, "Sec-WebSocket-Protocol");
if (ws_proto != NULL) {
printf("WebSocket upgrade completed\n"); mg_ws_upgrade(c, hm, ws_proto->buf);
} else {
} else if (mg_match(hm->uri, mg_str("/"), nullptr)) { mg_ws_upgrade(c, hm, NULL);
// Главная страница }
struct mg_http_serve_opts opts = { .root_dir = STATIC_DIR };
mg_http_serve_file(c, hm, "./static/index.html", &opts); printf("WebSocket upgrade completed\n");
} else if (mg_match(hm->uri, mg_str("/xterm.js"), nullptr)) { } else if (mg_match(hm->uri, mg_str("/"), nullptr)) {
// xterm.js // Главная страница
struct mg_http_serve_opts opts = { .root_dir = STATIC_DIR }; struct mg_http_serve_opts opts = {.root_dir = STATIC_DIR};
mg_http_serve_file(c, hm, "./static/xterm.js", &opts); mg_http_serve_file(c, hm, "./static/index.html", &opts);
} else if (mg_match(hm->uri, mg_str("/xterm.css"), nullptr)) { } else if (mg_match(hm->uri, mg_str("/xterm.js"), nullptr)) {
// xterm.css // xterm.js
struct mg_http_serve_opts opts = { .root_dir = STATIC_DIR }; struct mg_http_serve_opts opts = {.root_dir = STATIC_DIR};
mg_http_serve_file(c, hm, "./static/xterm.css", &opts); mg_http_serve_file(c, hm, "./static/xterm.js", &opts);
} else if (mg_match(hm->uri, mg_str("/xterm-addon-fit.js"), nullptr)) { } else if (mg_match(hm->uri, mg_str("/xterm.css"), nullptr)) {
// xterm-addon-fit.js // xterm.css
struct mg_http_serve_opts opts = { .root_dir = STATIC_DIR }; struct mg_http_serve_opts opts = {.root_dir = STATIC_DIR};
mg_http_serve_file(c, hm, "./static/xterm-addon-fit.js", &opts); mg_http_serve_file(c, hm, "./static/xterm.css", &opts);
} else { } else if (mg_match(hm->uri, mg_str("/xterm-addon-fit.js"), nullptr)) {
mg_http_reply(c, 404, "Content-Type: text/plain\r\n", "Not Found"); // xterm-addon-fit.js
} struct mg_http_serve_opts opts = {.root_dir = STATIC_DIR};
} else if (ev == MG_EV_WS_OPEN) { mg_http_serve_file(c, hm, "./static/xterm-addon-fit.js", &opts);
// WebSocket соединение установлено
printf("WebSocket connection opened\n"); } else {
struct client_data *cd = (struct client_data *)c->fn_data; mg_http_reply(c, 404, "Content-Type: text/plain\r\n", "Not Found");
if (cd) {
mg_ws_send(c, "Terminal ready - type 'exit' to restart session\r\n", 49, WEBSOCKET_OP_TEXT);
}
} else if (ev == MG_EV_WS_MSG) {
// Сообщение от WebSocket клиента
struct mg_ws_message *wm = (struct mg_ws_message *)ev_data;
struct client_data *cd = (struct client_data *)c->fn_data;
handle_websocket_message(c, wm, cd);
} else if (ev == MG_EV_CLOSE) {
// Закрытие соединения
struct client_data *cd = (struct client_data *)c->fn_data;
if (cd) {
printf("WebSocket connection closed, cleaning up...\n");
destroy_terminal_session(&cd->term);
free(cd);
c->fn_data = NULL;
}
} else if (ev == MG_EV_POLL) {
// Проверяем данные от терминала и отправляем через WebSocket
struct mg_connection *conn;
for (conn = mgr.conns; conn != NULL; conn = conn->next) {
if (!conn->is_websocket) continue;
struct client_data *cd = (struct client_data *)conn->fn_data;
if (cd && cd->terminal_ready) {
// Проверяем, не завершился ли процесс
if (!is_process_alive(cd->term.child_pid)) {
printf("Terminal process died, restarting...\n");
restart_terminal_session(conn, cd);
continue;
}
// Читаем данные из терминала
char buffer[1024];
ssize_t bytes_read = read_from_terminal(&cd->term, buffer, sizeof(buffer));
if (bytes_read > 0) {
mg_ws_send(conn, buffer, bytes_read, WEBSOCKET_OP_BINARY);
} else if (bytes_read == 0) {
// EOF - процесс завершился
printf("EOF from terminal, restarting...\n");
restart_terminal_session(conn, cd);
} else if (bytes_read < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
// Ошибка чтения
printf("Error reading from terminal: %s\n", strerror(errno));
restart_terminal_session(conn, cd);
}
}
}
} }
} else if (ev == MG_EV_WS_OPEN) {
// WebSocket соединение установлено
printf("WebSocket connection opened\n");
struct client_data *cd = (struct client_data *)c->fn_data;
if (cd) {
mg_ws_send(c, "Terminal ready - type 'exit' to restart session\r\n", 49,
WEBSOCKET_OP_TEXT);
}
} else if (ev == MG_EV_WS_MSG) {
// Сообщение от WebSocket клиента
struct mg_ws_message *wm = (struct mg_ws_message *)ev_data;
struct client_data *cd = (struct client_data *)c->fn_data;
handle_websocket_message(c, wm, cd);
} else if (ev == MG_EV_CLOSE) {
// Закрытие соединения
struct client_data *cd = (struct client_data *)c->fn_data;
if (cd) {
printf("WebSocket connection closed, cleaning up...\n");
destroy_terminal_session(&cd->term);
free(cd);
c->fn_data = NULL;
}
} else if (ev == MG_EV_POLL) {
// Проверяем данные от терминала и отправляем через WebSocket
struct mg_connection *conn;
for (conn = mgr.conns; conn != NULL; conn = conn->next) {
if (!conn->is_websocket)
continue;
struct client_data *cd = (struct client_data *)conn->fn_data;
if (cd && cd->terminal_ready) {
// Проверяем, не завершился ли процесс
if (!is_process_alive(cd->term.child_pid)) {
printf("Terminal process died, restarting...\n");
restart_terminal_session(conn, cd);
continue;
}
// Читаем данные из терминала
char buffer[1024];
ssize_t bytes_read =
read_from_terminal(&cd->term, buffer, sizeof(buffer));
if (bytes_read > 0) {
mg_ws_send(conn, buffer, bytes_read, WEBSOCKET_OP_BINARY);
} else if (bytes_read == 0) {
// EOF - процесс завершился
printf("EOF from terminal, restarting...\n");
restart_terminal_session(conn, cd);
} else if (bytes_read < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
// Ошибка чтения
printf("Error reading from terminal: %s\n", strerror(errno));
restart_terminal_session(conn, cd);
}
}
}
}
} }
// Обработчик сигналов для graceful shutdown // Обработчик сигналов для graceful shutdown
static void signal_handler(int sig) { static void signal_handler(int sig) {
printf("\nShutting down server...\n"); printf("\nShutting down server...\n");
// Очищаем все соединения // Очищаем все соединения
struct mg_connection *conn; struct mg_connection *conn;
for (conn = mgr.conns; conn != NULL; conn = conn->next) { for (conn = mgr.conns; conn != NULL; conn = conn->next) {
if (conn->fn_data) { if (conn->fn_data) {
struct client_data *cd = (struct client_data *)conn->fn_data; struct client_data *cd = (struct client_data *)conn->fn_data;
destroy_terminal_session(&cd->term); destroy_terminal_session(&cd->term);
free(cd); free(cd);
conn->fn_data = NULL; conn->fn_data = NULL;
}
} }
}
mg_mgr_free(&mgr);
exit(0); mg_mgr_free(&mgr);
exit(0);
} }
// Проверяем существование директории // Проверяем существование директории
static bool directory_exists(const char *path) { static bool directory_exists(const char *path) {
struct stat st; struct stat st;
return stat(path, &st) == 0 && S_ISDIR(st.st_mode); return stat(path, &st) == 0 && S_ISDIR(st.st_mode);
} }
int main(void) { int main(void) {
// Устанавливаем обработчики сигналов // Устанавливаем обработчики сигналов
signal(SIGINT, signal_handler); signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler); signal(SIGTERM, signal_handler);
// Инициализируем менеджер соединений // Инициализируем менеджер соединений
mg_mgr_init(&mgr); mg_mgr_init(&mgr);
printf("Mongoose version: %s\n", MG_VERSION); printf("Mongoose version: %s\n", MG_VERSION);
// Проверяем существование статической директории // Проверяем существование статической директории
if (!directory_exists(STATIC_DIR)) { if (!directory_exists(STATIC_DIR)) {
fprintf(stderr, "Static directory '%s' not found! Creating it...\n", STATIC_DIR); fprintf(stderr, "Static directory '%s' not found! Creating it...\n",
if (mkdir(STATIC_DIR, 0755) != 0) { STATIC_DIR);
fprintf(stderr, "Failed to create static directory\n"); if (mkdir(STATIC_DIR, 0755) != 0) {
return 1; fprintf(stderr, "Failed to create static directory\n");
} return 1;
} }
}
// Создаем HTTP сервер
struct mg_connection *c = mg_http_listen(&mgr, s_http_addr, fn, NULL); // Создаем HTTP сервер
if (c == NULL) { struct mg_connection *c = mg_http_listen(&mgr, s_http_addr, fn, NULL);
fprintf(stderr, "Failed to start server on %s\n", s_http_addr); if (c == NULL) {
return 1; fprintf(stderr, "Failed to start server on %s\n", s_http_addr);
} return 1;
}
printf("Starting web terminal server on %s\n", s_http_addr);
printf("Static files directory: %s\n", STATIC_DIR); printf("Starting web terminal server on %s\n", s_http_addr);
printf("Server is ready. Open http://localhost:8000 in your browser\n"); printf("Static files directory: %s\n", STATIC_DIR);
printf("Server is ready. Open http://localhost:8000 in your browser\n");
// Основной цикл
while (true) { // Основной цикл
mg_mgr_poll(&mgr, 50); // 50ms timeout while (true) {
} mg_mgr_poll(&mgr, 50); // 50ms timeout
}
mg_mgr_free(&mgr);
return 0; mg_mgr_free(&mgr);
return 0;
} }
+18 -18
View File
@@ -16361,7 +16361,7 @@ int mg_rsa_mod_pow(const uint8_t *mod, size_t modsz, const uint8_t *exp, size_t
#define REPEATM(N, macro) EVAL(REPEATM_SOME(N, macro)) #define REPEATM(N, macro) EVAL(REPEATM_SOME(N, macro))
#endif #endif
// //
#if (MG_UECC_WORD_SIZE == 1) #if (MG_UECC_WORD_SIZE == 1)
#if MG_UECC_SUPPORTS_secp160r1 #if MG_UECC_SUPPORTS_secp160r1
@@ -20899,7 +20899,7 @@ static bool cyw_wifi_connect(char *ssid, char *pass) {
uint32_t data[64/4 + 1]; // max pass length: 64 for WPA, 128 for WPA3 SAE uint32_t data[64/4 + 1]; // max pass length: 64 for WPA, 128 for WPA3 SAE
uint16_t *da = (uint16_t *) data; uint16_t *da = (uint16_t *) data;
unsigned int len; unsigned int len;
uint32_t val; uint32_t val;
val = 4; // security type: 0 for none, 2 for WPA, 4 for WPA2/WPA3, 6 for mixed WPA/WPA2 val = 4; // security type: 0 for none, 2 for WPA, 4 for WPA2/WPA3, 6 for mixed WPA/WPA2
// sup_wpa[1] = 0 if not using security // sup_wpa[1] = 0 if not using security
if (!(cyw_ioctl_set(134 /* SET_WSEC */, (uint8_t *)&val, sizeof(val)) if (!(cyw_ioctl_set(134 /* SET_WSEC */, (uint8_t *)&val, sizeof(val))
@@ -20941,7 +20941,7 @@ static bool cyw_wifi_ap_start(char *ssid, char *pass, unsigned int channel) {
uint32_t data[64/4 + 2]; // max pass length: 64 for WPA, 128 for WPA3 SAE uint32_t data[64/4 + 2]; // max pass length: 64 for WPA, 128 for WPA3 SAE
uint16_t *da = (uint16_t *) data; uint16_t *da = (uint16_t *) data;
unsigned int len; unsigned int len;
uint32_t val; uint32_t val;
// CHIP DEPENDENCY // CHIP DEPENDENCY
// RPi set the AMPDU parameter for AP (window size = 2) ***************** // RPi set the AMPDU parameter for AP (window size = 2) *****************
// val = 2 ; cyw_ioctl_iovar_set_(0, "ampdu_ba_wsize", (uint8_t *)&val, sizeof(val)); // val = 2 ; cyw_ioctl_iovar_set_(0, "ampdu_ba_wsize", (uint8_t *)&val, sizeof(val));
@@ -20970,10 +20970,10 @@ static bool cyw_wifi_ap_start(char *ssid, char *pass, unsigned int channel) {
da[1] = 1; // indicates wireless security key, skip for WPA3 SAE da[1] = 1; // indicates wireless security key, skip for WPA3 SAE
memcpy((uint8_t *)data + 2 * sizeof(uint16_t), pass, len); // skip for WPA3 SAE memcpy((uint8_t *)data + 2 * sizeof(uint16_t), pass, len); // skip for WPA3 SAE
if (!cyw_ioctl_set_(1, 268 /* SET_WSEC_PMK */, data, sizeof(data))) return false; // skip for WPA3 SAE, sizeof/2 if supporting SAE but using WPA if (!cyw_ioctl_set_(1, 268 /* SET_WSEC_PMK */, data, sizeof(data))) return false; // skip for WPA3 SAE, sizeof/2 if supporting SAE but using WPA
/* for WPA3 SAE: /* for WPA3 SAE:
memcpy((uint8_t *)data + sizeof(uint16_t), pass, len); memcpy((uint8_t *)data + sizeof(uint16_t), pass, len);
cyw_ioctl_iovar_set_(1, "sae_password", data, sizeof(data)); */ cyw_ioctl_iovar_set_(1, "sae_password", data, sizeof(data)); */
/* for WPA3 or mixed WPA2/WPA3: /* for WPA3 or mixed WPA2/WPA3:
val = 5 ; cyw_ioctl_iovar_set_(1, "sae_max_pwe_loop", (uint8_t *)&val, sizeof(val)); // Some chipsets do not support this */ val = 5 ; cyw_ioctl_iovar_set_(1, "sae_max_pwe_loop", (uint8_t *)&val, sizeof(val)); // Some chipsets do not support this */
// resume if not using auth // resume if not using auth
@@ -21119,7 +21119,7 @@ static void cyw_handle_scan_result(uint32_t status, struct scan_result *data, si
bss.band = band & CYW_BSS_BAND2G ? MG_WIFI_BAND_2G : MG_WIFI_BAND_5G; bss.band = band & CYW_BSS_BAND2G ? MG_WIFI_BAND_2G : MG_WIFI_BAND_5G;
bss.security = (sbss->capability & MG_BIT(4) /* CAP_PRIVACY */) ? MG_WIFI_SECURITY_WEP : MG_WIFI_SECURITY_OPEN; bss.security = (sbss->capability & MG_BIT(4) /* CAP_PRIVACY */) ? MG_WIFI_SECURITY_WEP : MG_WIFI_SECURITY_OPEN;
{ // travel IEs (Information Elements) in search of security definitions { // travel IEs (Information Elements) in search of security definitions
const uint8_t wot1[4] = {0x00, 0x50, 0xf2, 0x01}; // WPA_OUI_TYPE1 const uint8_t wot1[4] = {0x00, 0x50, 0xf2, 0x01}; // WPA_OUI_TYPE1
uint8_t *ie = (uint8_t *)sbss + sbss->ie_offset; uint8_t *ie = (uint8_t *)sbss + sbss->ie_offset;
int bytes = (int) sbss->ie_length; int bytes = (int) sbss->ie_length;
while (bytes > 0 && ie[1] + 2 < bytes) { // ie[0] -> type, ie[1] -> bytes from ie[2] while (bytes > 0 && ie[1] + 2 < bytes) { // ie[0] -> type, ie[1] -> bytes from ie[2]
@@ -21130,11 +21130,11 @@ static void cyw_handle_scan_result(uint32_t status, struct scan_result *data, si
bytes -= ie[1] + 2; bytes -= ie[1] + 2;
} }
} }
MG_VERBOSE(("BSS: %.*s (%u) (%M) %d dBm %u", bss.SSID.len, bss.SSID.buf, bss.channel, mg_print_mac, bss.BSSID, (int) bss.RSSI, bss.security)); MG_VERBOSE(("BSS: %.*s (%u) (%M) %d dBm %u", bss.SSID.len, bss.SSID.buf, bss.channel, mg_print_mac, bss.BSSID, (int) bss.RSSI, bss.security));
mg_tcpip_call(s_ifp, MG_TCPIP_EV_WIFI_SCAN_RESULT, &bss); mg_tcpip_call(s_ifp, MG_TCPIP_EV_WIFI_SCAN_RESULT, &bss);
} else { } else {
MG_ERROR(("scan error")); MG_ERROR(("scan error"));
} }
} }
// clang-format on // clang-format on
@@ -21326,7 +21326,7 @@ static bool cyw_init(uint8_t *mac) {
// CHIP DEPENDENCY // CHIP DEPENDENCY
val = 8 ; cyw_ioctl_iovar_set("ampdu_ba_wsize", (uint8_t *)&val, sizeof(val)); val = 8 ; cyw_ioctl_iovar_set("ampdu_ba_wsize", (uint8_t *)&val, sizeof(val));
val = 4 ; cyw_ioctl_iovar_set("ampdu_mpdu", (uint8_t *)&val, sizeof(val)); val = 4 ; cyw_ioctl_iovar_set("ampdu_mpdu", (uint8_t *)&val, sizeof(val));
val = 0 /* 8K */; cyw_ioctl_iovar_set("ampdu_rx_factor", (uint8_t *)&val, sizeof(val)); val = 0 /* 8K */; cyw_ioctl_iovar_set("ampdu_rx_factor", (uint8_t *)&val, sizeof(val));
// //
{ {
struct cyw_country c; struct cyw_country c;
@@ -21544,7 +21544,7 @@ static bool cyw_load_fwll(void *fwdata, size_t fwlen, void *nvramdata, size_t nv
cyw_load_data(CYW_CHIP_ATCMRAM_BASE, fwdata, fwlen); cyw_load_data(CYW_CHIP_ATCMRAM_BASE, fwdata, fwlen);
mg_delayms(5); // TODO(scaprile): CHECK IF THIS IS ACTUALLY NEEDED mg_delayms(5); // TODO(scaprile): CHECK IF THIS IS ACTUALLY NEEDED
// Load NVRAM and place 'length ~length' at the end; end of chip RAM // Load NVRAM and place 'length ~length' at the end; end of chip RAM
{ {
const uint32_t start = CYW_CHIP_RAM_SIZE - 4 - nvramlen; const uint32_t start = CYW_CHIP_RAM_SIZE - 4 - nvramlen;
cyw_load_data(start, nvramdata, nvramlen); // nvramlen must be a multiple of 4 cyw_load_data(start, nvramdata, nvramlen); // nvramlen must be a multiple of 4
// RAM_SIZE is a multiple of WINSZ, so the place for len ~len will be at the end of the window // RAM_SIZE is a multiple of WINSZ, so the place for len ~len will be at the end of the window
@@ -21835,7 +21835,7 @@ static bool cyw_sdio_init() {
struct mg_tcpip_sdio *s = (struct mg_tcpip_sdio *) d->bus; struct mg_tcpip_sdio *s = (struct mg_tcpip_sdio *) d->bus;
uint32_t val = 0; uint32_t val = 0;
if (!mg_sdio_init(s)) return false; if (!mg_sdio_init(s)) return false;
// no block transfers on F0. if (!mg_sdio_set_blksz(s, CYW_SDIO_FUNC_BUS, 32)) return false; // no block transfers on F0. if (!mg_sdio_set_blksz(s, CYW_SDIO_FUNC_BUS, 32)) return false;
if (!mg_sdio_set_blksz(s, CYW_SDIO_FUNC_CHIP, 64)) return false; if (!mg_sdio_set_blksz(s, CYW_SDIO_FUNC_CHIP, 64)) return false;
if (!mg_sdio_set_blksz(s, CYW_SDIO_FUNC_WLAN, 64)) return false; if (!mg_sdio_set_blksz(s, CYW_SDIO_FUNC_WLAN, 64)) return false;
// TODO(scaprile): we don't handle SDIO interrupts, study CCCR INTEN and SDIO support (SDIO 6.3, 8) // TODO(scaprile): we don't handle SDIO interrupts, study CCCR INTEN and SDIO support (SDIO 6.3, 8)
@@ -22052,7 +22052,7 @@ static bool mg_tcpip_driver_imxrt_init(struct mg_tcpip_if *ifp) {
int cr = (d == NULL || d->mdc_cr < 0) ? 24 : d->mdc_cr; int cr = (d == NULL || d->mdc_cr < 0) ? 24 : d->mdc_cr;
ENET->MSCR = (1 << 8) | ((cr & 0x3f) << 1); // HOLDTIME 2 clks ENET->MSCR = (1 << 8) | ((cr & 0x3f) << 1); // HOLDTIME 2 clks
struct mg_phy phy = {enet_read_phy, enet_write_phy}; struct mg_phy phy = {enet_read_phy, enet_write_phy};
mg_phy_init(&phy, d->phy_addr, MG_PHY_LEDS_ACTIVE_HIGH); // MAC clocks PHY mg_phy_init(&phy, d->phy_addr, MG_PHY_LEDS_ACTIVE_HIGH); // MAC clocks PHY
// Select RMII mode, 100M, keep CRC, set max rx length, disable loop // Select RMII mode, 100M, keep CRC, set max rx length, disable loop
ENET->RCR = (1518 << 16) | MG_BIT(8) | MG_BIT(2); ENET->RCR = (1518 << 16) | MG_BIT(8) | MG_BIT(2);
// ENET->RCR |= MG_BIT(3); // Receive all // ENET->RCR |= MG_BIT(3); // Receive all
@@ -24551,13 +24551,13 @@ struct tms570_mdio {
#define ETH_PKT_SIZE 1540 // Max frame size #define ETH_PKT_SIZE 1540 // Max frame size
#define ETH_DESC_CNT 4 // Descriptors count #define ETH_DESC_CNT 4 // Descriptors count
#define ETH_DS 4 // Descriptor size (words) #define ETH_DS 4 // Descriptor size (words)
static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS] static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]
__attribute__((section(".ETH_CPPI"), aligned(4))); // TX descriptors __attribute__((section(".ETH_CPPI"), aligned(4))); // TX descriptors
static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS] static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]
__attribute__((section(".ETH_CPPI"), aligned(4))); // RX descriptors __attribute__((section(".ETH_CPPI"), aligned(4))); // RX descriptors
static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE] static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]
__attribute__((aligned(4))); // RX ethernet buffers __attribute__((aligned(4))); // RX ethernet buffers
static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE] static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]
__attribute__((aligned(4))); // TX ethernet buffers __attribute__((aligned(4))); // TX ethernet buffers
static struct mg_tcpip_if *s_ifp; // MIP interface static struct mg_tcpip_if *s_ifp; // MIP interface
static uint16_t emac_read_phy(uint8_t addr, uint8_t reg) { static uint16_t emac_read_phy(uint8_t addr, uint8_t reg) {
@@ -24638,7 +24638,7 @@ static bool mg_tcpip_driver_tms570_init(struct mg_tcpip_if *ifp) {
} }
s_txdesc[ETH_DESC_CNT - 1][0] = 0; s_txdesc[ETH_DESC_CNT - 1][0] = 0;
s_rxdesc[ETH_DESC_CNT - 1][0] = 0; s_rxdesc[ETH_DESC_CNT - 1][0] = 0;
EMAC->MACCONTROL = MG_BIT(5) | MG_BIT(0); // Enable MII, Full-duplex EMAC->MACCONTROL = MG_BIT(5) | MG_BIT(0); // Enable MII, Full-duplex
//EMAC->TXINTMASKSET = 1; // Enable TX interrupt //EMAC->TXINTMASKSET = 1; // Enable TX interrupt
EMAC->RXINTMASKSET = 1; // Enable RX interrupt EMAC->RXINTMASKSET = 1; // Enable RX interrupt
@@ -24665,7 +24665,7 @@ static size_t mg_tcpip_driver_tms570_tx(const void *buf, size_t len,
s_txdesc[s_txno][2] = SWAP32((uint32_t) len); // Set data len s_txdesc[s_txno][2] = SWAP32((uint32_t) len); // Set data len
s_txdesc[s_txno][3] = s_txdesc[s_txno][3] =
SWAP32(MG_BIT(31) | MG_BIT(30) | MG_BIT(29) | len); // SOP, EOP, OWN, length SWAP32(MG_BIT(31) | MG_BIT(30) | MG_BIT(29) | len); // SOP, EOP, OWN, length
while(EMAC->TXHDP[0] != 0) (void) 0; while(EMAC->TXHDP[0] != 0) (void) 0;
EMAC->TXHDP[0] = (uint32_t) &s_txdesc[s_txno][0]; EMAC->TXHDP[0] = (uint32_t) &s_txdesc[s_txno][0];
if(++s_txno == ETH_DESC_CNT) { if(++s_txno == ETH_DESC_CNT) {
+757 -929
View File
File diff suppressed because it is too large. Load diff
+142 -142
View File
@@ -1,158 +1,158 @@
#include "terminal.h" #include "terminal.h"
#include <errno.h>
#include <fcntl.h>
#include <pty.h>
#include <signal.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <pty.h> #include <unistd.h>
#include <utmp.h> #include <utmp.h>
#include <signal.h>
#include <errno.h>
int create_terminal_session(struct terminal_session *session, const char *shell) { int create_terminal_session(struct terminal_session *session,
// Создаем pty const char *shell) {
int master, slave; // Создаем pty
char slave_name[64]; int master, slave;
char slave_name[64];
if (openpty(&master, &slave, slave_name, NULL, NULL) == -1) {
perror("openpty failed"); if (openpty(&master, &slave, slave_name, NULL, NULL) == -1) {
return -1; perror("openpty failed");
} return -1;
}
pid_t pid = fork();
if (pid == -1) { pid_t pid = fork();
perror("fork failed"); if (pid == -1) {
close(master); perror("fork failed");
close(slave); close(master);
return -1;
}
if (pid == 0) {
// Дочерний процесс
close(master);
// Устанавливаем pty как контролирующий терминал
setsid();
ioctl(slave, TIOCSCTTY, 1);
// Перенаправляем стандартные потоки
dup2(slave, STDIN_FILENO);
dup2(slave, STDOUT_FILENO);
dup2(slave, STDERR_FILENO);
if (slave > STDERR_FILENO) {
close(slave);
}
// Устанавливаем переменные окружения
setenv("TERM", "xterm-256color", 1);
setenv("COLORTERM", "truecolor", 1);
// Запускаем shell
execlp(shell, shell, NULL);
// Если execlp вернулся, значит произошла ошибка
perror("execlp failed");
exit(EXIT_FAILURE);
}
// Родительский процесс
close(slave); close(slave);
return -1;
session->master_pty = master; }
session->slave_pty = slave;
session->child_pid = pid; if (pid == 0) {
session->active = true; // Дочерний процесс
close(master);
// Устанавливаем неблокирующий режим
int flags = fcntl(master, F_GETFL, 0); // Устанавливаем pty как контролирующий терминал
fcntl(master, F_SETFL, flags | O_NONBLOCK); setsid();
ioctl(slave, TIOCSCTTY, 1);
printf("Created terminal session with PID %d\n", pid);
return 0; // Перенаправляем стандартные потоки
dup2(slave, STDIN_FILENO);
dup2(slave, STDOUT_FILENO);
dup2(slave, STDERR_FILENO);
if (slave > STDERR_FILENO) {
close(slave);
}
// Устанавливаем переменные окружения
setenv("TERM", "xterm-256color", 1);
setenv("COLORTERM", "truecolor", 1);
// Запускаем shell
execlp(shell, shell, NULL);
// Если execlp вернулся, значит произошла ошибка
perror("execlp failed");
exit(EXIT_FAILURE);
}
// Родительский процесс
close(slave);
session->master_pty = master;
session->slave_pty = slave;
session->child_pid = pid;
session->active = true;
// Устанавливаем неблокирующий режим
int flags = fcntl(master, F_GETFL, 0);
fcntl(master, F_SETFL, flags | O_NONBLOCK);
printf("Created terminal session with PID %d\n", pid);
return 0;
} }
void destroy_terminal_session(struct terminal_session *session) { void destroy_terminal_session(struct terminal_session *session) {
if (!session || !session->active) { if (!session || !session->active) {
return; return;
} }
printf("Destroying terminal session with PID %d\n", session->child_pid); printf("Destroying terminal session with PID %d\n", session->child_pid);
// Завершаем дочерний процесс // Завершаем дочерний процесс
if (session->child_pid > 0) { if (session->child_pid > 0) {
// Отправляем SIGKILL // Отправляем SIGKILL
int status; int status;
kill(session->child_pid, SIGKILL); kill(session->child_pid, SIGKILL);
waitpid(session->child_pid, &status, 0); waitpid(session->child_pid, &status, 0);
} }
// Закрываем pty // Закрываем pty
if (session->master_pty != -1) { if (session->master_pty != -1) {
close(session->master_pty); close(session->master_pty);
session->master_pty = -1; session->master_pty = -1;
} }
session->active = false; session->active = false;
session->child_pid = 0; session->child_pid = 0;
printf("Terminal session destroyed\n"); printf("Terminal session destroyed\n");
} }
ssize_t read_from_terminal(struct terminal_session *session, char *buffer, size_t size) { ssize_t read_from_terminal(struct terminal_session *session, char *buffer,
if (!session || !session->active) { size_t size) {
return -1; if (!session || !session->active) {
} return -1;
}
ssize_t bytes_read = read(session->master_pty, buffer, size - 1);
if (bytes_read > 0) {
buffer[bytes_read] = '\0';
} else if (bytes_read == 0) {
// EOF - соединение закрыто
return 0;
} else if (bytes_read < 0) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
perror("read from terminal failed");
return -1;
}
// Нет данных для чтения - это нормально в неблокирующем режиме
return -2;
}
return bytes_read;
}
ssize_t write_to_terminal(struct terminal_session *session, const char *data, size_t size) { ssize_t bytes_read = read(session->master_pty, buffer, size - 1);
if (!session || !session->active) { if (bytes_read > 0) {
return -1; buffer[bytes_read] = '\0';
} } else if (bytes_read == 0) {
// EOF - соединение закрыто
ssize_t bytes_written = write(session->master_pty, data, size);
if (bytes_written < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
perror("write to terminal failed");
return -1;
}
return bytes_written;
}
int resize_terminal(struct terminal_session *session, uint16_t rows, uint16_t cols) {
if (!session || !session->active) {
return -1;
}
struct winsize ws = {
.ws_row = rows,
.ws_col = cols,
.ws_xpixel = 0,
.ws_ypixel = 0
};
if (ioctl(session->master_pty, TIOCSWINSZ, &ws) == -1) {
perror("ioctl TIOCSWINSZ failed");
return -1;
}
return 0; return 0;
} } else if (bytes_read < 0) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
perror("read from terminal failed");
return -1;
}
// Нет данных для чтения - это нормально в неблокирующем режиме
return -2;
}
return bytes_read;
}
ssize_t write_to_terminal(struct terminal_session *session, const char *data,
size_t size) {
if (!session || !session->active) {
return -1;
}
ssize_t bytes_written = write(session->master_pty, data, size);
if (bytes_written < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
perror("write to terminal failed");
return -1;
}
return bytes_written;
}
int resize_terminal(struct terminal_session *session, uint16_t rows,
uint16_t cols) {
if (!session || !session->active) {
return -1;
}
struct winsize ws = {
.ws_row = rows, .ws_col = cols, .ws_xpixel = 0, .ws_ypixel = 0};
if (ioctl(session->master_pty, TIOCSWINSZ, &ws) == -1) {
perror("ioctl TIOCSWINSZ failed");
return -1;
}
return 0;
}
+14 -10
View File
@@ -1,25 +1,29 @@
#ifndef TERMINAL_H #ifndef TERMINAL_H
#define TERMINAL_H #define TERMINAL_H
#include <signal.h>
#include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#include <signal.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/types.h> #include <sys/types.h>
struct terminal_session { struct terminal_session {
int master_pty; int master_pty;
int slave_pty; int slave_pty;
pid_t child_pid; pid_t child_pid;
bool active; bool active;
}; };
// Функции для работы с терминалом // Функции для работы с терминалом
int create_terminal_session(struct terminal_session *session, const char *shell); int create_terminal_session(struct terminal_session *session,
const char *shell);
void destroy_terminal_session(struct terminal_session *session); void destroy_terminal_session(struct terminal_session *session);
ssize_t read_from_terminal(struct terminal_session *session, char *buffer, size_t size); ssize_t read_from_terminal(struct terminal_session *session, char *buffer,
ssize_t write_to_terminal(struct terminal_session *session, const char *data, size_t size); size_t size);
int resize_terminal(struct terminal_session *session, uint16_t rows, uint16_t cols); ssize_t write_to_terminal(struct terminal_session *session, const char *data,
size_t size);
int resize_terminal(struct terminal_session *session, uint16_t rows,
uint16_t cols);
#endif #endif
+187 -168
View File
@@ -1,176 +1,195 @@
<!DOCTYPE html> <!doctype html>
<html> <html>
<head> <head>
<title>Web Terminal</title> <title>Web Terminal</title>
<link rel="stylesheet" href="xterm.css"> <link rel="stylesheet" href="xterm.css" />
<style> <style>
body { body {
margin: 0; margin: 0;
padding: 20px; padding: 20px;
background-color: #1e1e1e; background-color: #1e1e1e;
color: #fff; color: #fff;
font-family: Arial, sans-serif; font-family: Arial, sans-serif;
height: 100vh; height: 100vh;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
#terminal-container {
flex: 1;
border: 1px solid #333;
border-radius: 5px;
overflow: hidden;
margin-top: 10px;
}
.status {
padding: 10px;
border-radius: 3px;
font-weight: bold;
margin-bottom: 10px;
}
.connected { background-color: #2d5a2d; }
.disconnected { background-color: #5a2d2d; }
.connecting { background-color: #5a5a2d; }
</style>
</head>
<body>
<h1>Web Terminal</h1>
<div class="status connecting" id="status">Connecting...</div>
<div id="terminal-container"></div>
<script src="xterm.js"></script> #terminal-container {
<script src="xterm-addon-fit.js"></script> flex: 1;
<script> border: 1px solid #333;
class WebTerminal { border-radius: 5px;
constructor() { overflow: hidden;
this.terminal = new Terminal({ margin-top: 10px;
cursorBlink: true,
theme: {
background: '#000000',
foreground: '#ffffff',
cursor: '#ffffff'
}
});
this.fitAddon = new FitAddon.FitAddon();
this.terminal.loadAddon(this.fitAddon);
this.ws = null;
this.reconnectAttempts = 0;
this.maxReconnectAttempts = 5;
this.reconnectDelay = 3000;
this.init();
} }
init() { .status {
const terminalContainer = document.getElementById('terminal-container'); padding: 10px;
this.terminal.open(terminalContainer); border-radius: 3px;
this.fitAddon.fit(); font-weight: bold;
margin-bottom: 10px;
this.terminal.writeln('Welcome to Web Terminal');
this.terminal.writeln('Connecting to server...');
this.connect();
// Handle terminal input
this.terminal.onData((data) => {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
this.ws.send(data);
}
});
// Handle resize
window.addEventListener('resize', () => {
this.fitAddon.fit();
this.sendResize();
});
// Handle page unload
window.addEventListener('beforeunload', () => {
if (this.ws) {
this.ws.close();
}
});
} }
connect() { .connected {
this.updateStatus('Connecting...', 'connecting'); background-color: #2d5a2d;
}
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; .disconnected {
const wsUrl = `${protocol}//${window.location.host}/ws`; background-color: #5a2d2d;
}
this.ws = new WebSocket(wsUrl); .connecting {
background-color: #5a5a2d;
this.ws.onopen = () => { }
console.log('WebSocket connected'); </style>
</head>
<body>
<h1>Web Terminal</h1>
<div class="status connecting" id="status">Connecting...</div>
<div id="terminal-container"></div>
<script src="xterm.js"></script>
<script src="xterm-addon-fit.js"></script>
<script>
class WebTerminal {
constructor() {
this.terminal = new Terminal({
cursorBlink: true,
theme: {
background: "#000000",
foreground: "#ffffff",
cursor: "#ffffff",
},
});
this.fitAddon = new FitAddon.FitAddon();
this.terminal.loadAddon(this.fitAddon);
this.ws = null;
this.reconnectAttempts = 0; this.reconnectAttempts = 0;
this.updateStatus('Connected', 'connected'); this.maxReconnectAttempts = 5;
this.terminal.clear(); this.reconnectDelay = 3000;
this.sendResize();
}; this.init();
}
this.ws.onmessage = (event) => {
if (typeof event.data === 'string') { init() {
this.terminal.write(event.data); const terminalContainer =
} else { document.getElementById("terminal-container");
// Handle binary data this.terminal.open(terminalContainer);
const reader = new FileReader(); this.fitAddon.fit();
reader.onload = () => {
const data = new Uint8Array(reader.result); this.terminal.writeln("Welcome to Web Terminal");
this.terminal.write(data); this.terminal.writeln("Connecting to server...");
};
reader.readAsArrayBuffer(event.data); this.connect();
}
}; // Handle terminal input
this.terminal.onData((data) => {
this.ws.onclose = (event) => { if (this.ws && this.ws.readyState === WebSocket.OPEN) {
console.log('WebSocket disconnected:', event.code, event.reason); this.ws.send(data);
this.updateStatus('Disconnected', 'disconnected'); }
});
if (this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnectAttempts++; // Handle resize
const delay = this.reconnectDelay * this.reconnectAttempts; window.addEventListener("resize", () => {
this.terminal.write(`\r\nConnection lost. Reconnecting in ${delay/1000}s...\r\n`); this.fitAddon.fit();
this.sendResize();
setTimeout(() => { });
this.connect();
}, delay); // Handle page unload
} else { window.addEventListener("beforeunload", () => {
this.terminal.write('\r\nFailed to reconnect. Please refresh the page.\r\n'); if (this.ws) {
} this.ws.close();
}; }
});
this.ws.onerror = (error) => { }
console.error('WebSocket error:', error);
this.updateStatus('Connection Error', 'disconnected'); connect() {
}; this.updateStatus("Connecting...", "connecting");
}
const protocol =
sendResize() { window.location.protocol === "https:" ? "wss:" : "ws:";
if (this.ws && this.ws.readyState === WebSocket.OPEN) { const wsUrl = `${protocol}//${window.location.host}/ws`;
const dimensions = this.terminal;
const resizeMsg = { this.ws = new WebSocket(wsUrl);
cols: dimensions.cols,
rows: dimensions.rows this.ws.onopen = () => {
console.log("WebSocket connected");
this.reconnectAttempts = 0;
this.updateStatus("Connected", "connected");
this.terminal.clear();
this.sendResize();
}; };
this.ws.send(JSON.stringify(resizeMsg));
this.ws.onmessage = (event) => {
if (typeof event.data === "string") {
this.terminal.write(event.data);
} else {
// Handle binary data
const reader = new FileReader();
reader.onload = () => {
const data = new Uint8Array(reader.result);
this.terminal.write(data);
};
reader.readAsArrayBuffer(event.data);
}
};
this.ws.onclose = (event) => {
console.log(
"WebSocket disconnected:",
event.code,
event.reason,
);
this.updateStatus("Disconnected", "disconnected");
if (
this.reconnectAttempts < this.maxReconnectAttempts
) {
this.reconnectAttempts++;
const delay =
this.reconnectDelay * this.reconnectAttempts;
this.terminal.write(
`\r\nConnection lost. Reconnecting in ${delay / 1000}s...\r\n`,
);
setTimeout(() => {
this.connect();
}, delay);
} else {
this.terminal.write(
"\r\nFailed to reconnect. Please refresh the page.\r\n",
);
}
};
this.ws.onerror = (error) => {
console.error("WebSocket error:", error);
this.updateStatus("Connection Error", "disconnected");
};
}
sendResize() {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
const dimensions = this.terminal;
const resizeMsg = {
cols: dimensions.cols,
rows: dimensions.rows,
};
this.ws.send(JSON.stringify(resizeMsg));
}
}
updateStatus(text, className) {
const status = document.getElementById("status");
status.textContent = text;
status.className = `status ${className}`;
} }
} }
updateStatus(text, className) { // Initialize terminal when page loads
const status = document.getElementById('status'); document.addEventListener("DOMContentLoaded", () => {
status.textContent = text; new WebTerminal();
status.className = `status ${className}`; });
} </script>
} </body>
</html>
// Initialize terminal when page loads
document.addEventListener('DOMContentLoaded', () => {
new WebTerminal();
});
</script>
</body>
</html>
+73 -2
View File
@@ -1,2 +1,73 @@
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.FitAddon=t():e.FitAddon=t()}(self,(()=>(()=>{"use strict";var e={};return(()=>{var t=e;Object.defineProperty(t,"__esModule",{value:!0}),t.FitAddon=void 0,t.FitAddon=class{activate(e){this._terminal=e}dispose(){}fit(){const e=this.proposeDimensions();if(!e||!this._terminal||isNaN(e.cols)||isNaN(e.rows))return;const t=this._terminal._core;this._terminal.rows===e.rows&&this._terminal.cols===e.cols||(t._renderService.clear(),this._terminal.resize(e.cols,e.rows))}proposeDimensions(){if(!this._terminal)return;if(!this._terminal.element||!this._terminal.element.parentElement)return;const e=this._terminal._core,t=e._renderService.dimensions;if(0===t.css.cell.width||0===t.css.cell.height)return;const r=0===this._terminal.options.scrollback?0:e.viewport.scrollBarWidth,i=window.getComputedStyle(this._terminal.element.parentElement),o=parseInt(i.getPropertyValue("height")),s=Math.max(0,parseInt(i.getPropertyValue("width"))),n=window.getComputedStyle(this._terminal.element),l=o-(parseInt(n.getPropertyValue("padding-top"))+parseInt(n.getPropertyValue("padding-bottom"))),a=s-(parseInt(n.getPropertyValue("padding-right"))+parseInt(n.getPropertyValue("padding-left")))-r;return{cols:Math.max(2,Math.floor(a/t.css.cell.width)),rows:Math.max(1,Math.floor(l/t.css.cell.height))}}}})(),e})())); !(function (e, t) {
//# sourceMappingURL=xterm-addon-fit.js.map "object" == typeof exports && "object" == typeof module
? (module.exports = t())
: "function" == typeof define && define.amd
? define([], t)
: "object" == typeof exports
? (exports.FitAddon = t())
: (e.FitAddon = t());
})(self, () =>
(() => {
"use strict";
var e = {};
return (
(() => {
var t = e;
(Object.defineProperty(t, "__esModule", { value: !0 }),
(t.FitAddon = void 0),
(t.FitAddon = class {
activate(e) {
this._terminal = e;
}
dispose() {}
fit() {
const e = this.proposeDimensions();
if (!e || !this._terminal || isNaN(e.cols) || isNaN(e.rows))
return;
const t = this._terminal._core;
(this._terminal.rows === e.rows &&
this._terminal.cols === e.cols) ||
(t._renderService.clear(),
this._terminal.resize(e.cols, e.rows));
}
proposeDimensions() {
if (!this._terminal) return;
if (
!this._terminal.element ||
!this._terminal.element.parentElement
)
return;
const e = this._terminal._core,
t = e._renderService.dimensions;
if (0 === t.css.cell.width || 0 === t.css.cell.height) return;
const r =
0 === this._terminal.options.scrollback
? 0
: e.viewport.scrollBarWidth,
i = window.getComputedStyle(
this._terminal.element.parentElement,
),
o = parseInt(i.getPropertyValue("height")),
s = Math.max(0, parseInt(i.getPropertyValue("width"))),
n = window.getComputedStyle(this._terminal.element),
l =
o -
(parseInt(n.getPropertyValue("padding-top")) +
parseInt(n.getPropertyValue("padding-bottom"))),
a =
s -
(parseInt(n.getPropertyValue("padding-right")) +
parseInt(n.getPropertyValue("padding-left"))) -
r;
return {
cols: Math.max(2, Math.floor(a / t.css.cell.width)),
rows: Math.max(1, Math.floor(l / t.css.cell.height)),
};
}
}));
})(),
e
);
})(),
);
//# sourceMappingURL=xterm-addon-fit.js.map
+37 -15
View File
@@ -79,7 +79,7 @@
.xterm .composition-view { .xterm .composition-view {
/* TODO: Composition position got messed up somewhere */ /* TODO: Composition position got messed up somewhere */
background: #000; background: #000;
color: #FFF; color: #fff;
display: none; display: none;
position: absolute; position: absolute;
white-space: nowrap; white-space: nowrap;
@@ -166,33 +166,55 @@
opacity: 1 !important; opacity: 1 !important;
} }
.xterm-underline-1 { text-decoration: underline; } .xterm-underline-1 {
.xterm-underline-2 { text-decoration: double underline; } text-decoration: underline;
.xterm-underline-3 { text-decoration: wavy underline; } }
.xterm-underline-4 { text-decoration: dotted underline; } .xterm-underline-2 {
.xterm-underline-5 { text-decoration: dashed underline; } text-decoration: double underline;
}
.xterm-underline-3 {
text-decoration: wavy underline;
}
.xterm-underline-4 {
text-decoration: dotted underline;
}
.xterm-underline-5 {
text-decoration: dashed underline;
}
.xterm-overline { .xterm-overline {
text-decoration: overline; text-decoration: overline;
} }
.xterm-overline.xterm-underline-1 { text-decoration: overline underline; } .xterm-overline.xterm-underline-1 {
.xterm-overline.xterm-underline-2 { text-decoration: overline double underline; } text-decoration: overline underline;
.xterm-overline.xterm-underline-3 { text-decoration: overline wavy underline; } }
.xterm-overline.xterm-underline-4 { text-decoration: overline dotted underline; } .xterm-overline.xterm-underline-2 {
.xterm-overline.xterm-underline-5 { text-decoration: overline dashed underline; } text-decoration: overline double underline;
}
.xterm-overline.xterm-underline-3 {
text-decoration: overline wavy underline;
}
.xterm-overline.xterm-underline-4 {
text-decoration: overline dotted underline;
}
.xterm-overline.xterm-underline-5 {
text-decoration: overline dashed underline;
}
.xterm-strikethrough { .xterm-strikethrough {
text-decoration: line-through; text-decoration: line-through;
} }
.xterm-screen .xterm-decoration-container .xterm-decoration { .xterm-screen .xterm-decoration-container .xterm-decoration {
z-index: 6; z-index: 6;
position: absolute; position: absolute;
} }
.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer { .xterm-screen
z-index: 7; .xterm-decoration-container
.xterm-decoration.xterm-decoration-top-layer {
z-index: 7;
} }
.xterm-decoration-overview-ruler { .xterm-decoration-overview-ruler {
+14698 -2
View File
File diff suppressed because it is too large. Load diff