diff --git a/jim.LICENSE b/jim.LICENSE new file mode 100644 index 0000000..8458563 --- /dev/null +++ b/jim.LICENSE @@ -0,0 +1,20 @@ +Copyright 2021 Alexey Kutepov + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/jimp.h b/jimp.h new file mode 100644 index 0000000..995a05c --- /dev/null +++ b/jimp.h @@ -0,0 +1,448 @@ +// Prototype of an Immediate Deserialization idea. Expect this API to change a lot. +#ifndef JIMP_H_ +#define JIMP_H_ + +#include +#include +#include +#include +#include +#include +#include + +// TODO: move all diagnostics reporting outside of the library +// So the user has more options on how to report things + +typedef enum { + JIMP_INVALID, + JIMP_EOF, + + // Puncts + JIMP_OCURLY, + JIMP_CCURLY, + JIMP_OBRACKET, + JIMP_CBRACKET, + JIMP_COMMA, + JIMP_COLON, + + // Symbols + JIMP_TRUE, + JIMP_FALSE, + JIMP_NULL, + + // Values + JIMP_STRING, + JIMP_NUMBER, +} Jimp_Token; + +typedef struct { + const char *file_path; + const char *start; + const char *end; + const char *point; + + Jimp_Token token; + const char *token_start; // TODO: `token_start` is primarily used for diagnostics location. Rename it accordingly. + + char *string; + size_t string_count; + size_t string_capacity; + double number; + bool boolean; +} Jimp; + +// TODO: how do null-s fit into this entire system? + +void jimp_begin(Jimp *jimp, const char *file_path, const char *input, size_t input_size); + +/// If succeeds puts the freshly parsed boolean into jimp->boolean. +/// Any consequent calls to the jimp_* functions may invalidate jimp->boolean. +bool jimp_bool(Jimp *jimp); + +/// If succeeds puts the freshly parsed number into jimp->number. +/// Any consequent calls to the jimp_* functions may invalidate jimp->number. +bool jimp_number(Jimp *jimp); + +/// If succeeds puts the freshly parsed string into jimp->string as a NULL-terminated string. +/// Any consequent calls to the jimp_* functions may invalidate jimp->string. +/// strdup it if you don't wanna lose it (memory management is on you at that point). +bool jimp_string(Jimp *jimp); + +/// Parses the beginning of the object `{` +bool jimp_object_begin(Jimp *jimp); + +/// If succeeds puts the key of the member into jimp->string as a NULL-terminated string. +/// Any consequent calls to the jimp_* functions may invalidate jimp->string. +/// strdup it if you don't wanna lose it (memory management is on you at that point). +bool jimp_object_member(Jimp *jimp); + +/// Parses the end of the object `}` +bool jimp_object_end(Jimp *jimp); + +/// Reports jimp->string as an unknown member. jimp->string is expected to be populated by +/// jimp_object_member. +void jimp_unknown_member(Jimp *jimp); + +/// Parses the beginning of the array `[` +bool jimp_array_begin(Jimp *jimp); + +/// Checks whether there is any more items in the array. +bool jimp_array_item(Jimp *jimp); + +/// Parses the end of the array `]` +bool jimp_array_end(Jimp *jimp); + +/// Prints diagnostic at the current position of the parser. +void jimp_diagf(Jimp *jimp, const char *fmt, ...); + +bool jimp_is_null_ahead(Jimp *jimp); +bool jimp_is_bool_ahead(Jimp *jimp); +bool jimp_is_number_ahead(Jimp *jimp); +bool jimp_is_string_ahead(Jimp *jimp); +bool jimp_is_array_ahead(Jimp *jimp); +bool jimp_is_object_ahead(Jimp *jimp); + +#endif // JIMP_H_ + +#ifdef JIMP_IMPLEMENTATION + +static bool jimp__expect_token(Jimp *jimp, Jimp_Token token); +static bool jimp__get_and_expect_token(Jimp *jimp, Jimp_Token token); +static const char *jimp__token_kind(Jimp_Token token); +static bool jimp__get_token(Jimp *jimp); +static void jimp__skip_whitespaces(Jimp *jimp); +static void jimp__append_to_string(Jimp *jimp, char x); + +static void jimp__append_to_string(Jimp *jimp, char x) +{ + if (jimp->string_count >= jimp->string_capacity) { + if (jimp->string_capacity == 0) jimp->string_capacity = 1024; + else jimp->string_capacity *= 2; + jimp->string = realloc(jimp->string, jimp->string_capacity); + } + jimp->string[jimp->string_count++] = x; +} + +static void jimp__skip_whitespaces(Jimp *jimp) +{ + while (jimp->point < jimp->end && isspace(*jimp->point)) { + jimp->point += 1; + } +} + +static Jimp_Token jimp__puncts[256] = { + ['{'] = JIMP_OCURLY, + ['}'] = JIMP_CCURLY, + ['['] = JIMP_OBRACKET, + [']'] = JIMP_CBRACKET, + [','] = JIMP_COMMA, + [':'] = JIMP_COLON, +}; + +static struct { + Jimp_Token token; + const char *symbol; +} jimp__symbols[] = { + { .token = JIMP_TRUE, .symbol = "true" }, + { .token = JIMP_FALSE, .symbol = "false" }, + { .token = JIMP_NULL, .symbol = "null" }, +}; +#define jimp__symbols_count (sizeof(jimp__symbols)/sizeof(jimp__symbols[0])) + +static bool jimp__get_token(Jimp *jimp) +{ + jimp__skip_whitespaces(jimp); + + jimp->token_start = jimp->point; + + if (jimp->point >= jimp->end) { + jimp->token = JIMP_EOF; + return false; + } + + jimp->token = jimp__puncts[(unsigned char)*jimp->point]; + if (jimp->token) { + jimp->point += 1; + return true; + } + + for (size_t i = 0; i < jimp__symbols_count; ++i) { + const char *symbol = jimp__symbols[i].symbol; + if (*symbol == *jimp->point) { + while (*symbol && jimp->point < jimp->end && *symbol++ == *jimp->point++) {} + if (*symbol) { + jimp->token = JIMP_INVALID; + jimp_diagf(jimp, "ERROR: invalid symbol\n"); + return false; + } else { + jimp->token = jimp__symbols[i].token; + return true; + } + } + } + + char *endptr = NULL; + jimp->number = strtod(jimp->point, &endptr); // TODO: This implies that jimp->end is a valid address and *jimp->end == 0 + if (jimp->point != endptr) { + jimp->point = endptr; + jimp->token = JIMP_NUMBER; + return true; + } + + if (*jimp->point == '"') { + jimp->point++; + jimp->string_count = 0; + while (jimp->point < jimp->end) { + // TODO: support all the JSON escape sequences defined in the spec + // Yes, including those dumb suroggate pairs. Spec is spec. + switch (*jimp->point) { + case '\\': { + jimp->point++; + if (jimp->point >= jimp->end) { + jimp->token_start = jimp->point; + jimp_diagf(jimp, "ERROR: unfinished escape sequence\n"); + return false; + } + switch (*jimp->point) { + case 'r': + jimp->point++; + jimp__append_to_string(jimp, '\r'); + break; + case 'n': + jimp->point++; + jimp__append_to_string(jimp, '\n'); + break; + case 't': + jimp->point++; + jimp__append_to_string(jimp, '\t'); + break; + case '\\': + jimp->point++; + jimp__append_to_string(jimp, '\\'); + break; + case '"': + jimp->point++; + jimp__append_to_string(jimp, '"'); + break; + default: + jimp->token_start = jimp->point; + jimp_diagf(jimp, "ERROR: invalid escape sequence\n"); + return false; + } + break; + } + case '"': { + jimp->point++; + jimp__append_to_string(jimp, '\0'); + jimp->token = JIMP_STRING; + return true; + } + default: { + char x = *jimp->point++; + jimp__append_to_string(jimp, x); + } + } + } + jimp->token = JIMP_INVALID; + jimp_diagf(jimp, "ERROR: unfinished string\n"); + return false; + } + + jimp->token = JIMP_INVALID; + jimp_diagf(jimp, "ERROR: invalid token\n"); + return false; +} + +void jimp_begin(Jimp *jimp, const char *file_path, const char *input, size_t input_size) +{ + jimp->file_path = file_path; + jimp->start = input; + jimp->end = input + input_size; + jimp->point = input; +} + +void jimp_diagf(Jimp *jimp, const char *fmt, ...) +{ + long line_number = 0; + const char *line_start = jimp->start; + const char *point = jimp->start; + while (point < jimp->token_start) { + char x = *point++; + if (x == '\n') { + line_start = point; + line_number += 1; + } + } + + fprintf(stderr, "%s:%ld:%ld: ", jimp->file_path, line_number + 1, point - line_start + 1); + va_list args; + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); +} + +static const char *jimp__token_kind(Jimp_Token token) +{ + switch (token) { + case JIMP_EOF: return "end of input"; + case JIMP_INVALID: return "invalid"; + case JIMP_OCURLY: return "{"; + case JIMP_CCURLY: return "}"; + case JIMP_OBRACKET: return "["; + case JIMP_CBRACKET: return "]"; + case JIMP_COMMA: return ","; + case JIMP_COLON: return ":"; + case JIMP_TRUE: return "true"; + case JIMP_FALSE: return "false"; + case JIMP_NULL: return "null"; + case JIMP_STRING: return "string"; + case JIMP_NUMBER: return "number"; + } + assert(0 && "unreachable"); + return NULL; +} + +bool jimp_array_begin(Jimp *jimp) +{ + return jimp__get_and_expect_token(jimp, JIMP_OBRACKET); +} + +bool jimp_array_end(Jimp *jimp) +{ + return jimp__get_and_expect_token(jimp, JIMP_CBRACKET); +} + +bool jimp_array_item(Jimp *jimp) +{ + const char *point = jimp->point; + if (!jimp__get_token(jimp)) return false; + if (jimp->token == JIMP_COMMA) return true; + if (jimp->token == JIMP_CBRACKET) { + jimp->point = point; + return false; + } + jimp->point = point; + return true; +} + +void jimp_unknown_member(Jimp *jimp) +{ + jimp_diagf(jimp, "ERROR: unexpected object member `%s`\n", jimp->string); +} + +bool jimp_object_begin(Jimp *jimp) +{ + return jimp__get_and_expect_token(jimp, JIMP_OCURLY); +} + +bool jimp_object_member(Jimp *jimp) +{ + const char *point = jimp->point; + if (!jimp__get_token(jimp)) return false; + if (jimp->token == JIMP_COMMA) { + if (!jimp__get_and_expect_token(jimp, JIMP_STRING)) return false; + if (!jimp__get_and_expect_token(jimp, JIMP_COLON)) return false; + return true; + } + if (jimp->token == JIMP_CCURLY) { + jimp->point = point; + return false; + } + if (!jimp__expect_token(jimp, JIMP_STRING)) return false; + if (!jimp__get_and_expect_token(jimp, JIMP_COLON)) return false; + return true; +} + +bool jimp_object_end(Jimp *jimp) +{ + return jimp__get_and_expect_token(jimp, JIMP_CCURLY); +} + +bool jimp_string(Jimp *jimp) +{ + return jimp__get_and_expect_token(jimp, JIMP_STRING); +} + +bool jimp_bool(Jimp *jimp) +{ + jimp__get_token(jimp); + if (jimp->token == JIMP_TRUE) { + jimp->boolean = true; + } else if (jimp->token == JIMP_FALSE) { + jimp->boolean = false; + } else { + jimp_diagf(jimp, "ERROR: expected boolean, but got `%s`\n", jimp__token_kind(jimp->token)); + return false; + } + return true; +} + +bool jimp_number(Jimp *jimp) +{ + return jimp__get_and_expect_token(jimp, JIMP_NUMBER); +} + +bool jimp_is_null_ahead(Jimp *jimp) +{ + const char *point = jimp->point; + if (!jimp__get_token(jimp)) return false; + jimp->point = point; + return jimp->token == JIMP_NULL; +} + +bool jimp_is_bool_ahead(Jimp *jimp) +{ + const char *point = jimp->point; + if (!jimp__get_token(jimp)) return false; + jimp->point = point; + return jimp->token == JIMP_TRUE || jimp->token == JIMP_FALSE; +} + +bool jimp_is_number_ahead(Jimp *jimp) +{ + const char *point = jimp->point; + if (!jimp__get_token(jimp)) return false; + jimp->point = point; + return jimp->token == JIMP_NUMBER; +} + +bool jimp_is_string_ahead(Jimp *jimp) +{ + const char *point = jimp->point; + if (!jimp__get_token(jimp)) return false; + jimp->point = point; + return jimp->token == JIMP_STRING; +} + +bool jimp_is_array_ahead(Jimp *jimp) +{ + const char *point = jimp->point; + if (!jimp__get_token(jimp)) return false; + jimp->point = point; + return jimp->token == JIMP_OBRACKET; +} + +bool jimp_is_object_ahead(Jimp *jimp) +{ + const char *point = jimp->point; + if (!jimp__get_token(jimp)) return false; + jimp->point = point; + return jimp->token == JIMP_OCURLY; +} + +static bool jimp__get_and_expect_token(Jimp *jimp, Jimp_Token token) +{ + if (!jimp__get_token(jimp)) return false; + return jimp__expect_token(jimp, token); +} + +static bool jimp__expect_token(Jimp *jimp, Jimp_Token token) +{ + if (jimp->token != token) { + jimp_diagf(jimp, "ERROR: expected %s, but got %s\n", jimp__token_kind(token), jimp__token_kind(jimp->token)); + return false; + } + return true; +} + +#endif // JIMP_IMPLEMENTATION diff --git a/main.c b/main.c index b9308d7..fc80fc1 100644 --- a/main.c +++ b/main.c @@ -1,62 +1,123 @@ #include "main.h" +void print_data(Data *data) +{ + printf("TYPE: %d\n", data->type); + if (data->request != 0) + printf("REQUEST: %d\n", data->request); + if (data->response != 0) + printf("RESPONSE: %d\n", data->response); + if (data->hostname != NULL) + printf("HOSTNAME: %s\n", data->hostname); +} + +bool parse_data(char *buf, Data *data) +{ + Jimp jimp = {0}; + jimp.start = buf; + jimp.end = buf + strlen(buf); + jimp.point = buf; + + if (!jimp_object_begin(&jimp)) return false; + + while (jimp_object_member(&jimp)) { + if (strcmp(jimp.string, "TYPE") == 0) { + if (!jimp_number(&jimp)) + return false; + data->type = jimp.number; + } else if (strcmp(jimp.string, "REQUEST") == 0) { + if (jimp__get_and_expect_token(&jimp, JIMP_NULL)) + continue; + if (!jimp_number(&jimp)) + return false; + data->request = jimp.number; + } else if (strcmp(jimp.string, "RESPONSE") == 0) { + if (jimp__get_and_expect_token(&jimp, JIMP_NULL)) + continue; + if (!jimp_number(&jimp)) + return false; + data->response = jimp.number; + } else if (strcmp(jimp.string, "HOSTNAME") == 0) { + if (jimp__get_and_expect_token(&jimp, JIMP_NULL)) + continue; + if (!jimp_string(&jimp)) + return false; + data->hostname = jimp.string; + } + } + + print_data(data); + return true; +}; + int main(void) { - const int fd = socket(PF_INET, SOCK_STREAM, 0); + const int fd = socket(PF_INET, SOCK_STREAM, 0); - struct sockaddr_in addr = { 0 }; - addr.sin_family = AF_INET; - addr.sin_port = htons((short) PORT); + struct sockaddr_in addr = { 0 }; + addr.sin_family = AF_INET; + addr.sin_port = htons((short) PORT); - // connect to local machine at specified port - char addrstr[NI_MAXHOST + NI_MAXSERV + 1]; - snprintf(addrstr, sizeof(addrstr), "127.0.0.1:%d", PORT); + // connect to local machine at specified port + char addrstr[NI_MAXHOST + NI_MAXSERV + 1]; + snprintf(addrstr, sizeof(addrstr), "127.0.0.1:%d", PORT); - // parse into address - inet_pton(AF_INET, addrstr, &addr.sin_addr); + // parse into address + inet_pton(AF_INET, addrstr, &addr.sin_addr); - // connect to server - if (connect(fd, (struct sockaddr*) &addr, sizeof(addr))) { - perror("failed to connect"); - return -1; - } + // connect to server + if (connect(fd, (struct sockaddr*) &addr, sizeof(addr))) { + perror("failed to connect"); + return 1; + } char hostname[HOST_NAME_MAX]; int res = gethostname(hostname, HOST_NAME_MAX); if (res != 0) { perror("ERROR: failed to get hostname"); - return -1; + return 1; } - /* - { - "TYPE": 1, - "HOSTNAME": "%S", - "DATA": { - } - } - */ - Jim jim = {.pp = 4}; jim_object_begin(&jim); jim_member_key(&jim, "TYPE"); - jim_integer(&jim, 0); + jim_integer(&jim, REQUEST); jim_member_key(&jim, "REQUEST"); - jim_integer(&jim, 0); + jim_integer(&jim, NEW_SERVER_CLIENT); jim_member_key(&jim, "HOSTNAME"); jim_string(&jim, hostname); jim_object_end(&jim); - printf("%s\n", jim.sink); + printf("sending initial request to server\n"); + send(fd, jim.sink, jim.sink_count, 0); - send(fd, jim.sink, jim.sink_count, 0); + char buf[MAX_MESSAGE_SIZE]; + res = recv(fd, buf, MAX_MESSAGE_SIZE, 0); + if (res == -1) { + fprintf(stderr, "ERROR: failed to receive message from server\n"); + return 1; + } + + printf("%s\n", buf); + Data data = {0}; + parse_data(buf, &data); + if (data.type != RESPONSE) { + fprintf(stderr, "ERROR: server didn't send a result, exiting...\n"); + close(fd); + exit(1); + } else if (data.response == ERROR) { + fprintf(stderr, "ERROR: server encounted error, exiting...\n"); + close(fd); + exit(1); + } - for (;;) { + printf("connected to server\n"); + while (true) { sleep(INTERVAL); } - close(fd); + close(fd); return 0; } diff --git a/main.h b/main.h index 5b9ab25..42d2918 100644 --- a/main.h +++ b/main.h @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -8,7 +9,46 @@ #define JIM_IMPLEMENTATION #include "jim.h" +#define JIMP_IMPLEMENTATION +#include "jimp.h" + +#define SYSINFO_IMPLEMENTATION #include "sysinfo.h" #define PORT 7778 +#define MAX_MESSAGE_SIZE 1024 + +enum Type { + REQUEST = 1, + RESPONSE = 2, +}; + +enum Request_Type { + NEW_SERVER_CLIENT = 1, + NEW_DESKTOP_CLIENT = 2, + DATA = 3, +}; + +enum Response_Type { + OK = 1, + CREATED = 2, + ERROR = 3, +}; + +typedef struct { + long timestamp; + double cpu_usage_percentage; + double memory_used_gb; + double memory_total_gb; + double swap_used_gb; + double swap_total_gb; +} Monitoring_Data; + +typedef struct { + enum Type type; + enum Request_Type request; + enum Response_Type response; + char *hostname; + Monitoring_Data monitoring_data; +} Data; diff --git a/sysinfo.h b/sysinfo.h index 544f5ef..5782757 100644 --- a/sysinfo.h +++ b/sysinfo.h @@ -7,35 +7,19 @@ #include #include -#define MAX_MESSAGE_SIZE 1024 - #define INTERVAL 10 -char *string_insert(char *str1, const char *str2, int pos); int get_cpu_usage_percentage(void); int get_ram_usage_percentage(void); void get_formatted_date_time(char *format, char *formattedDateTime); double get_available_space_from_mountpoint_gb(const char *mountpoint); double get_total_space_from_mountpoint_gb(const char *mountpoint); double get_used_space_from_mountpoint_gb(const char *mountpoint); +double get_used_space_from_mountpoint_percentage(const char *mountpoint); int get_number_of_processes(void); #ifdef SYSINFO_IMPLEMENTATION -char *string_insert(char *str1, const char *str2, int pos) -{ - size_t l1 = strlen(str1); - size_t l2 = strlen(str2); - - if (pos < 0) pos = 0; - if (pos > l1) pos = l1; - - char *p = str1 + pos; - memmove(p + l2, p, l1 - pos); - memcpy (p, str2, l2); - return str1; -} - int get_cpu_usage_percentage(void) { int perc; @@ -50,7 +34,7 @@ int get_cpu_usage_percentage(void) fscanf(fp, "%*s %Lf %Lf %Lf %Lf", &a[0], &a[1], &a[2], &a[3]); fclose(fp); - sleep(delay); + sleep(INTERVAL); fp = fopen("/proc/stat", "r"); if (fp == NULL) { @@ -120,9 +104,9 @@ double get_used_space_from_mountpoint_gb(const char *mountpoint) return (((long double)mountpointStatfs.f_blocks * mountpointStatfs.f_bsize) - ((long double)mountpointStatfs.f_bfree * mountpointStatfs.f_bsize))/1073741824; } -double get_used_space_from_mountpoint_gb(const char *mountpoint) +double get_used_space_from_mountpoint_percentage(const char *mountpoint) { - return (mountpointUsedGB(mountpoint)/mountpointTotalGB(mountpoint)) * 100; + return (get_used_space_from_mountpoint_gb(mountpoint)/get_total_space_from_mountpoint_gb(mountpoint)) * 100; } #endif