#include #include #include #include #include #include #include #include #include // #include // handle Ctrl-C/SIGINT #include // strcasecmp #include // usleep(), nanonsleep() ? #include #include // random() #include /* Log level guideline: * - ZF_LOG_FATAL - happened something impossible and absolutely unexpected. * Process can't continue and must be terminated. * Example: division by zero, unexpected modifications from other thread. * - ZF_LOG_ERROR - happened something possible, but highly unexpected. The * process is able to recover and continue execution. * Example: out of memory (could also be FATAL if not handled properly). * - ZF_LOG_WARN - happened something that *usually* should not happen and * significantly changes application behavior for some period of time. * Example: configuration file not found, auth error. * - ZF_LOG_INFO - happened significant life cycle event or major state * transition. * Example: app started, user logged in. * - ZF_LOG_DEBUG - minimal set of events that could help to reconstruct the * execution path. Usually disabled in release builds. * - ZF_LOG_VERBOSE - all other events. Usually disabled in release builds. * * *Ideally*, log file of debugged, well tested, production ready application * should be empty or very small. Choosing a right log level is as important as * providing short and self descriptive log message. */ /* #define ZF_LOG_VERBOSE 1 #define ZF_LOG_DEBUG 2 #define ZF_LOG_INFO 3 #define ZF_LOG_WARN 4 #define ZF_LOG_ERROR 5 #define ZF_LOG_FATAL 6 */ // LOGGING with file output #include "zf_log.h" FILE *g_log_file; static void file_output_callback(const zf_log_message *msg, void *arg) { (void)arg; *msg->p = '\n'; fwrite(msg->buf, msg->p - msg->buf + 1, 1, g_log_file); fflush(g_log_file); } static void file_output_close(void) { fclose(g_log_file); } static void file_output_open(const char *const log_path) { g_log_file = fopen(log_path, "a"); if (!g_log_file) { ZF_LOGW("Failed to open log file %s", log_path); return; } atexit(file_output_close); zf_log_set_output_v(ZF_LOG_PUT_STD, 0, file_output_callback); } #include "terminal.h" struct console_details console; #include "utils.h" // END LOGGING #include "lastseen.h" /* What is the name of the actual, real Mystic executable that we'll be executing and mangling? */ #define TARGET "./mySTIC" // Size of our input and output buffers. #define BSIZE 1024 /* * string_insert() * Inserts a string into a given position. * This safely checks to make sure the buffer isn't overrun. */ int string_insert(char *buffer, int max_length, int pos, const char *insert) { assert(max_length > pos); assert(buffer != NULL); assert(insert != NULL); if (strlen(buffer) + strlen(insert) >= max_length) { ZF_LOGD("string_insert() failed inserting [%s]", repr(insert)); return 0; } memmove(buffer + pos + strlen(insert), buffer + pos, strlen(buffer + pos) + 1); // cp + strlen(display), cp, strlen(cp) + 1); strncpy(buffer + pos, insert, strlen(insert)); // (cp, display, strlen(display)); return 1; // success } // Should this be passed around to the functions that use it? #include "render.h" /* These are harry "timeout" events. These happen when we've been sitting around awhile. */ #ifdef CPP_MADMAN_STL_CODE const char *random_phrase(const char *words, int len, int last_seen) { // ooh. a map of char *s to last_seen_events. :P static map> tracker; map>::iterator it; array it = tracker.find(words); if (it == tracker.end()) { // key does not exist. array last; for (int i = 0; i < last_seen; i++) { last[i] = -1; }; tracker.insert(words, last); it = tracker.find(words); }; } #endif void harry_idle_event(int fd) { // Make something happen char buffer[100]; int r; // This is no where near finished, BUT! const char *phrases[] = {"Hahaha", "Snicker, snicker", "Boo!", "MeOW", "I see U", "Arrooo!", "Ahh-wooo!", "Aaaooo!"}; const char *cp; static LastSeen last_seen_harry_event(2); // Remember the last phrase used, // and don't repeat (the last two)! do { r = randint((sizeof(phrases) / sizeof(char *))); // r = random() % ((sizeof(phrases) / sizeof(char *)) - 1); } while (last_seen_harry_event.seen_before(r)); // ZF_LOGD("%d => %d %d", r, last_seen_harry_event[0], // last_seen_harry_event[1]); cp = phrases[r]; int color = random() % 15 + 1; /* int color = random() % 16; if (color == 0) { color++; } // If it's 0 let's make it 1. // color = (random() % 15) + 1 */ sprintf(buffer, "^S2^C%02d%s^P2^CR^D%02d", color, cp, (int)strlen(cp)); ZF_LOGD("harry_event: render(%d, \"%s\")", fd, buffer); render(fd, buffer, strlen(buffer)); } void init_harry() { // init_have_seen(last_seen_harry_event, MAX_HARRY_EVENT_DUPS); // ZF_LOGD("init => %d %d", last_seen_harry_event[0], // last_seen_harry_event[1]); console_init(&console); } /* The code to get the username and fullname is useless on telnet connections. */ const char *username = NULL; const char *fullname = NULL; /* Pascal String Copy. Copy from pascal string, to C String. First char is pascal string length. (Max 255). */ void pcopy(char *pstring, char *str) { int len = (int)*pstring; strncpy(str, pstring + 1, len); str[len] = 0; } /* This only works for those few idiots that use the horribly broken SSH crap that Mystic uses. */ int locate_user(const char *alias) { FILE *user; char buffer[0x600]; char temp[100]; user = fopen("data/users.dat", "rb"); if (user == NULL) return 0; // Carry on! while (fread(buffer, 0x600, 1, user) == 1) { pcopy(buffer + 0x6d, temp); if (strcasecmp(temp, username) == 0) { pcopy(buffer + 0x8c, temp); fullname = strdup(temp); break; } /* printf("Alias: %s\n", temp); pcopy(buffer + 0x8c, temp ); printf("Full Name: %s\n", temp ); */ } fclose(user); return 1; } // Buffers are BSIZE + 1, so a buffer that size can strcpy safely. regex_t ANSI; regex_t WORDS; regex_t WORD; int init_regex(void) { int ret; char ansi[] = "\x1b\[[0-9]+(;[0-9]+)*?[a-zA-Z]"; char words[] = "[a-zA-Z]+( [a-zA-Z]+)+"; char word[] = "[a-zA-Z]+"; char errorbuf[100]; if (ret = regcomp(&ANSI, ansi, REG_EXTENDED | REG_NEWLINE)) { regerror(ret, &ANSI, errorbuf, sizeof(errorbuf)); ZF_LOGW("Regex %s failed to compile: %s", ansi, errorbuf); return 0; }; if (ret = regcomp(&WORDS, words, REG_EXTENDED | REG_NEWLINE)) { regerror(ret, &WORDS, errorbuf, sizeof(errorbuf)); ZF_LOGW("Regex %s failed to compile: %s", words, errorbuf); return 0; }; if (ret = regcomp(&WORD, word, REG_EXTENDED | REG_NEWLINE)) { regerror(ret, &WORD, errorbuf, sizeof(errorbuf)); ZF_LOGW("Regex %s failed to compile: %s", word, errorbuf); return 0; }; return 1; } int regmatch(regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags) { // returns number of matches found. (Max nmatch) int matches = 0; int offset = 0; while (matches < nmatch) { int ret = regexec(preg, string + offset, nmatch - matches, pmatch + matches, eflags); if (!ret) { int current = offset; offset += pmatch[matches].rm_eo; pmatch[matches].rm_so += current; pmatch[matches].rm_eo += current; matches++; } else if (ret == REG_NOMATCH) { break; } else { break; } } return matches; } #define MAX_MATCH 32 regmatch_t rxmatch[MAX_MATCH]; int rx_match(regex_t *regex, const char *buffer) { int ret; ret = regmatch(regex, buffer, MAX_MATCH, rxmatch, 0); if (0) { for (int i = 0; i < ret; i++) { ZF_LOGI("%d : (%d-%d)", i, rxmatch[i].rm_so, rxmatch[i].rm_eo); } } return ret; } /** * random_activate() * * Is a weight (1-10), * tests if random number is < weight * 10. * * So random_activate(9) happens more frequently * then random_activate(8) or lower. * * This probably needs to be fixed. * We need a better randint(RANGE) code. */ int random_activate(int w) { int r = randint(100); if (r <= (w * 10)) { return 1; }; return 0; } /* word_state(): // deprecated -1 only lower +1 only upper 0 mixed */ int word_state(const char *buffer, int len) { int p; int upper = 0; int lower = 0; int ret; float pct; for (p = 0; p < len; p++) { char c = buffer[p]; if (isalpha(c)) { if (isupper(c)) { upper++; }; if (islower(c)) { lower++; }; } } if (upper == lower) { return 0; } if (upper > lower) { ret = 1; pct = ((float)lower / (float)upper) * 100.0; } else { ret = -1; pct = ((float)upper / (float)lower) * 100.0; } // ZF_LOGD("So far %d with %f %%", ret, pct); if (pct < 40.0) { return ret; } return 0; } /* Given a buffer and length, mangle away. toupper, tolower, flipper */ int word_mangler(char *buffer, int len) { int p; int count = 0; int state; // state = word_state(buffer, len); // ZF_LOGD("word_state(%.*s) %d", len, buffer, state); state = randrange(-1, 1); // TODO: Transposer for (p = 0; p < len; p++) { char c = buffer[p]; if (randint(len) == p) { break; } switch (state) { case -1: // upper if (islower(c)) { count++; buffer[p] = toupper(c); } break; case 1: // lower if (isupper(c)) { count++; buffer[p] = tolower(c); } break; case 0: // flipper if (islower(c)) { count++; buffer[p] = toupper(c); } else { if (isupper(c)) { count++; buffer[p] = tolower(c); } } break; } } return count; } int word_wrangler(char *buffer, int len) { int p; int count; int state; // state = word_state(buffer, len); // ZF_LOGD("word_state(%.*s) %d", len, buffer, state); if (len < 5) { return 0; } p = randint(len - 4) + 2; for (count = 0; count < 4; count++) { if (!isalpha(buffer[p + count])) break; } ZF_LOGV_MEM(buffer, len, "wrangler %d len %d:", p, count); if (count >= 2) { for (int x = 0; x < count / 2; x++) { char ch = buffer[p + x]; buffer[p + x] = buffer[p + count - 1 - x]; buffer[p + count - 1 - x] = ch; } ZF_LOGV_MEM(buffer, len, "word now:"); return 1; } else return 0; } int buffer_insert(char *buffer, int len, int max_length, int pos, const char *insert) { if (len + strlen(insert) > max_length) { ZF_LOGD("buffer_insert failed [%s]", repr(insert)); return 0; } memmove(buffer + pos + strlen(insert), buffer + pos, len - pos); strncpy(buffer + pos, insert, strlen(insert)); return 1; } /* * The buffer that we've been given is much larger now. * * We can no longer mangle or insert into the given buffer. * Why? Because we don't know what is behind it now! */ int mangle(int fd, const char *buffer, int len) { int x, i; int need_render = 0; // changing word case around doesn't need the render int mangled = 0; int mangled_chars = 0; char play[BSIZE * 2]; const char *cp; // Make a copy of buffer, since it can no longer be changed // inserted into. memcpy(play, buffer, len); // NEVER reference buffer from this point on! char work[BSIZE * 2]; // Use terminal - clean out ANSI // ZF_LOGI("mangle:"); ZF_LOGI_MEM(play, len, "Mangle (%u bytes):", len); // strcpy(work, buffer); /* NOTE: We copy the buffer, so we can clear out ANSI codes, etc. Otherwise we might mess some ANSI up in the manglying process. */ /* (random) Look for ANSI CLS and: display random spooky texts around, with delays ... then CLS. display ANSI graphic file, with delays ... then CLS */ const char *ANSI_CLS = "\x1b[2J"; cp = strnstr(play, len, ANSI_CLS); // strstr(buffer, ANSI_CLS); if (cp != NULL) { static int ANSI_CLS_count = 0; // count the number we've seen ZF_LOGI("seen: ANSI_CLS"); ANSI_CLS_count++; // Don't activate on the very first CLS. Too soon, don't screw up the ANSI // detection. if (ANSI_CLS_count > 1) { // Ok, figure out the restore color, just in case struct console_details temp_console; // Make exact copy of our current console state. memcpy(&temp_console, &console, sizeof(console)); // Play the buffer into the console console_receive(&temp_console, play, cp - play); char restore_color[30]; // ansi color strcpy(restore_color, color_restore(&temp_console)); if (random_activate(3)) { char display[100] = ""; int needs_cls = 0; struct file_need { const char *filename; int cls; int rand_pos; } possibles[] = { {"goofy_head", 1, 0}, {"bat", 1, 0}, {"panther", 1, 0}, {"wolf", 1, 0}, {"skull", 0, 1}, {"skull2", 0, 1}, {"guy", 0, 1}, {"blinkman", 0, 1}, {"ghost", 1, 0}}; static LastSeen last_files(2); int r; do { r = randint((sizeof(possibles) / sizeof(file_need))); } while (last_files.seen_before(r)); int x, y; x = randint(50) + 1; y = randint(12) + 1; char fgoto[10]; if (possibles[r].rand_pos) { sprintf(fgoto, "^f%02d%02d", x, y); } else { strcpy(fgoto, "^F"); } // (2); // (sizeof(possibles) / sizeof(file_need)) - 1); needs_cls = possibles[r].cls; // I get what's happening. Mystic moves cursor to home, CLS, cursor // home. When we get here, we're ALWAYS at the top of the screen... // Hence our bat isn't displayed at the end of the screen. // This is before the actual CLS, so we CLS before displaying our files. // I tried a ^P2 before doing this .. but I'd rather have the picture up // right away I think. sprintf(display, "%s%s%s.^P3%s", needs_cls ? "\x1b[2J" : "", fgoto, possibles[r].filename, restore_color); ZF_LOGI("mangle(ANSI_CLS): %d file inserted %s", r, repr(display)); // Move the buffer so there's room for the display string. if (buffer_insert(play, len, sizeof(play), cp - play, display)) { len += strlen(display); // if (string_insert(buffer, 1024, cp - buffer, display)) { ZF_LOGI_MEM(play, len, "mangle(ANSI_CLS) (%u bytes):", len); // ZF_LOGI("mangle(ANSI_CLS):"); // ZF_LOGI_REPR(buffer); // ZF_LOGI("mangle(ANSI_CLS): [%s]", repr(buffer)); need_render = 1; /* Copy the new buffer over, but hide our "render" code from the remaining mangler steps. */ memcpy(work, play, len); // strcpy(work, buffer); i = cp - play; // find offset into "buffer" // apply to work. memset(work + i, ' ', strlen(display)); } else { ZF_LOGD("insert failed [%s].", repr(display)); } } else { if (random_activate(4)) { int r; char display[100] = ""; const char *phrasing[] = {"^R1Haha^P1ha^P1ha", "Poof!", "Got U", "Anyone there?", "^R1Knock, ^P1Knock"}; static LastSeen last_phrasing(2); ZF_LOGI("mangle(ANSI_CLS)"); // sprintf( display, "^P2..."); // This string actually screws up ANSI detection (takes too long) // strcpy(display, "^P2^S501234567890^P1abcdef^P2g^P3h^P4i^S0^P2"); // strcpy(display, "^P2^S301234^P15^S0^P2"); // Add in random text, plus color! do { r = random() % ((sizeof(phrasing) / sizeof(char *)) - 1); } while (last_phrasing.seen_before(r)); int color = random() % 15 + 1; int x = random() % 30 + 1; int y = random() % 15 + 1; /* Don't have it pause there before moving the cursor. Move the cursor, get the color changed, THEN pause. Then act all crazy. NOTE: Make sure if you use any ^R Render effects, turn them off before trying to display the restore_color. :P ^R0 Also, make sure you re-home the cursor ^G0101 because that's where they are expecting the cursor to be! (At least it's how Mystic does it.) HOME, CLS, HOME, ... Not sure what others do there. We'll see. */ sprintf(display, "^G%02d%02d^S3^C%02d^P1%s^S0^R0%s^P1^G0101", x, y, color, phrasing[r], restore_color); // sprintf(display, "^P1^S3^C%02d%s^S0^R0%s^P1", color, phrasing[r], // restore_color); // ZF_LOGI("mangle(ANSI_CLS): Inserted (%02d) %s", color, // phrasing[r]); ZF_LOGI_MEM(play, len, "mangle(ANSI_CLS) :"); // Move the buffer so there's room for the display string. if (buffer_insert(play, len, sizeof(play), cp - play, display)) { len += strlen(display); // if (string_insert(buffer, BSIZE * 4, cp - buffer, display)) { ZF_LOGI_MEM(play, len, "mangle(ANSI_CLS) + :"); // ZF_LOGI("mangle(ANSI_CLS):"); // ZF_LOGI_REPR(buffer); need_render = 1; /* Copy the new buffer over, but hide our "render" code from the remaining mangler steps. */ memcpy(work, play, len); // strcpy(work, buffer); i = cp - play; // find offset into "buffer" // apply to work. memset(work + i, ' ', strlen(display)); } else { ZF_LOGD("insert failed [%s].", repr(display)); } } } } } memcpy(work, play, len); // strcpy(work, buffer); // sure. const char replace_with = ' '; for (x = 0; x < len; x++) { int ansi = console_char(&console, play[x]); if (ansi) { work[x] = replace_with; } // fixup "work" so it's a valid C string if (buffer[x] == 0) { work[x] = replace_with; } } // fixup "work" buffer so it's a valid c string // (required for regex to work.) work[len] = 0; ZF_LOGI_MEM(work, len, "Work now:"); /* (random) Locate words (in work), and possibly flip them around. Transpose words. Transpose case. Transpose letters. Ok, what would be interesting, is if we could find W\x1[0;34mORDS with color changes in them, and work with them. without screwing up the color changes, of course. :P Example: Y\x1b[0;1mes \x1b[0;1;34m\x1b[0;1;34;44m N\x1b[0;1;44mo Yes No ^ This would be a job for a crazy regex. I'd have to map the characters to positions in the buffer. :S I'd want mangle and wrangle to work. The Message menu -- doesn't hardly get mangled at all (at least on my test site). Because all of the color changes break up the words in the menu. */ x = rx_match(&WORDS, work); ZF_LOGD("found %d word groups", x); if (x > 0) { for (i = 0; i < x; i++) { // Yes! Be random! if (random_activate(8)) { int c = word_mangler(play + rxmatch[i].rm_so, rxmatch[i].rm_eo - rxmatch[i].rm_so); if (c) { mangled++; mangled_chars += c; } } if (random_activate(4)) { word_wrangler(play + rxmatch[i].rm_so, rxmatch[i].rm_eo - rxmatch[i].rm_so); } } } /* (random) Locate single words, and transpose words. Transpose letters. */ /* (random) Display up to certain point. Delay. Print some characters slowly. Delay. */ if (need_render) { ZF_LOGD_MEM(play, len, "Ready to render:"); // ZF_LOGD("HH %d : (%d) %s", need_render, (int)strlen(buffer), // repr(buffer)); } else { if (mangled) { ZF_LOGD_MEM(play, len, "Mangled %d words, %d chars:", mangled, mangled_chars); /* ZF_LOGD("Mangled %d words, %d chars : %s", mangled, mangled_chars, repr(buffer)); */ } } if (need_render) { render(fd, play, len); } else { write(fd, play, len); // strlen(buffer)); }; return need_render && mangled; } int harry_happens(time_t *last_event, int wakeup) { time_t now = time(NULL); int elapsed = now - *last_event; if (elapsed > wakeup) { // Ok! It's been too long since we've done something. *last_event = now; return 1; } return 0; } /* TO FIX: Stop using c strings, must use char * buffer + int length. MAY CONTAIN NULL VALUES. Rework some things here. Here's the "plan": if buffer is EMPTY: time_idle = 1; // setup for "random timeout value mess" // we're in luck! The last parameter is time interval/timeout. :D timeout.tv_sec = 10; // randrange(10-25) timeout.tv_usec = 0; NOT EMPTY: // we're in luck! The last parameter is time interval/timeout. :D timeout.tv_sec = 0; timeout.tv_usec = 10; // Wild Guess Here? Maybe higher, maybe lower? time_idle = 0; ON READ: read/append to current buffer. We can't use nulls -- what if they are using ZModem, there's nulls in the file! Look for trailing / the very last "\r\n". (I could mangle/chunk it line by line. But I'm not sure I'd need to do that.) Optional "mangle" buffer up to that very point -- and send up to that point. Option #2: Maybe we send everything if program has been running for under 20 seconds. This would allow the ANSI detect to not get screwed up by this new idea. ON TIMEOUT: if time_idle: Activate funny harry timeout events. else: Ok, we *STILL* haven't received any more characters into the buffer -- even after waiting. (Maybe we haven't waited long enough?) send the pending information in the buffer and clear it out. Maybe this is a prompt, and there won't be a \r\n. This allows for cleaner process of "lines" of buffer. We shouldn't break in the midDLE OF A WORD. Downside is that we sit on buffer contents a little while / some amount of time -- which will add some lag to prompts showing up. ZModem: start: "rz^M**"... 05-12 18:12:15.916 >> rz^M**^XB00000000000000^M<8A>^Q 05-12 18:12:15.928 << **\x18B0100000023be50\r\n\x11 05-12 18:12:15.928 >> *^XC^D 05-12 18:12:15.939 << **\x18B0900000000a87c\r\n\x11 05-12 18:12:15.940 >> *^XC # Start of PK zipfile. 05-12 18:12:15.941 >> PK^C^D^T end: 05-12 18:26:38.700 << **\x18B0100000023be50\r\n\x11 05-12 18:26:38.700 >> **^XB0823a77600344c^M<8A> 05-12 18:26:38.711 << **\x18B0800000000022d\r\n 05-12 18:26:38.712 >> OO^MESC[0m */ int main(int argc, char *argv[]) { int master; pid_t pid; int node = -1; file_output_open("horrible_harry.log"); init_harry(); srandom(time(NULL)); // ./mystic -TID7 -IP192.168.0.1 -HOSTUnknown -ML1 -SL0 -ST2 -CUnknown // -Ubugz -PUWISHPASSWORD // ./mystic -TID7 -IP192.168.0.1 -HOSTUnknown -ML0 -SL0 -ST0 -CUnknown // ./mystic -TID7 -IP192.168.0.1 -HOSTUnknown -ML1 -SL0 -ST2 -CUnknown // -Ubugz -PUWISH // ./mystic -TID7 -IP192.168.0.1 -HOSTUnknown -ML0 -SL0 -ST0 -CUnknown // ./mystic -TID7 -IP192.168.0.1 -HOSTUnknown -ML0 -SL0 -ST0 -CUnknown // ./mystic -TID9 -IP192.168.0.1 -HOSTUnknown -ML0 -SL1 -ST0 -CUnknown // ./mystic -TID7 -IP192.168.0.1 -HOSTUnknown -ML1 -SL0 -ST2 -CUnknown // -Ubugz -PDUMBWAYTODOTHIS // ./mystic -TID9 -IP192.168.0.1 -HOSTUnknown -ML1 -SL1 -ST2 -CUnknown // -Ubugz -PIDONTUSEPASCAL // SSH: -ML1 -ST2 // Telnet: -ML0 -ST0 // Locate username (if given) in the command line // -U for (int x = 0; x < argc; x++) { if (strncmp("-U", argv[x], 2) == 0) { username = argv[x] + 2; ZF_LOGI("Username: [%s]", username); }; if (strncmp("-SL", argv[x], 3) == 0) { node = argv[x][3] - '0' + 1; ZF_LOGI("Node: %d", node); } } if (username != NULL) { locate_user(username); ZF_LOGD("Username: [%s] A.K.A. [%s]", username, fullname); } if (!init_regex()) return 2; pid = forkpty(&master, NULL, NULL, NULL); // impossible to fork if (pid < 0) { return 1; } // child else if (pid == 0) { char *args[20]; // max 20 args int x; char new_exec[] = TARGET; args[0] = new_exec; for (x = 1; x < argc; x++) { args[x] = argv[x]; }; args[x] = NULL; // run Mystic, run! execvp(TARGET, args); } // parent else { struct termios tios, orig1; struct timeval timeout; time_t last_event = 0; // time(NULL); ZF_LOGD("starting"); tcgetattr(master, &tios); tios.c_lflag &= ~(ECHO | ECHONL | ICANON); /* tios.c_iflag &= ~(ICRNL | IXON | BRKINT); tios.c_iflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); tios.c_oflag &= ~(OPOST); */ tcsetattr(master, TCSAFLUSH, &tios); tcgetattr(1, &orig1); tios = orig1; tios.c_iflag &= ~(ICRNL | IXON | BRKINT); tios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); tios.c_oflag &= ~(OPOST); // https://viewsourcecode.org/snaptoken/kilo/02.enteringRawMode.html tcsetattr(1, TCSAFLUSH, &tios); char buffer[BSIZE + 1]; int size = 0; for (;;) { int time_idle; // define estruturas para o select, que serve para verificar qual // se tornou "pronto pra uso" fd_set read_fd; fd_set write_fd; fd_set except_fd; // inicializa as estruturas FD_ZERO(&read_fd); FD_ZERO(&write_fd); FD_ZERO(&except_fd); // atribui o descritor master, obtido pelo forkpty, ao read_fd FD_SET(master, &read_fd); // atribui o stdin ao read_fd FD_SET(STDIN_FILENO, &read_fd); // o descritor tem que ser unico para o programa, a documentacao // recomenda um calculo entre os descritores sendo usados + 1 /* TODO: Figure out how this would work. I'm thinking something like timeouts 30-50 seconds? And as we get closer, 15-25 seconds. */ if (size == 0) { // buffer is empty timeout.tv_sec = randrange(10, 20); timeout.tv_usec = 0; time_idle = 1; } else { // buffer is not empty timeout.tv_sec = 0; timeout.tv_usec = 1; time_idle = 0; } if (select(master + 1, &read_fd, &write_fd, &except_fd, &timeout) == 0) { ZF_LOGI("TIMEOUT"); // This means timeout! if (time_idle) { harry_idle_event(STDOUT_FILENO); } else { ZF_LOGV_MEM(buffer, size, "TIMEOUT buffer size=%d", size); console_receive(&console, buffer, size); write(STDOUT_FILENO, buffer, size); size = 0; // buffer is empty now } } /* static char output[(BSIZE * 4) + 1]; */ int total; // read_fd esta atribuido com read_fd? if (FD_ISSET(master, &read_fd)) { // leia o que bc esta mandando // ZF_LOGD("read (%d) %d bytes", size, BSIZE - size); if ((total = read(master, buffer + size, BSIZE - size)) != -1) { // Ok, we've read more into the buffer. ZF_LOGV_MEM(buffer + size, total, "Read %d bytes:", total); size += total; ZF_LOGV_MEM(buffer, size, "Buffer now:"); int pos = rstrnstr(buffer, size, "\r\n"); if (pos >= 0) { // found something! pos += 2; ZF_LOGD_MEM(buffer, pos, "mangle buffer %d bytes:", pos); mangle(STDOUT_FILENO, buffer, pos); memmove(buffer, buffer + pos, size - pos); size -= pos; } else { ZF_LOGV("position of /r/n not found."); } } else break; } // read_fd esta atribuido com a entrada padrao? if (FD_ISSET(STDIN_FILENO, &read_fd)) { // leia a entrada padrao char input[BSIZE]; int r = read(STDIN_FILENO, &input, BSIZE); input[r] = 0; // e escreva no bc ZF_LOGI("<< %s", repr(input)); write(master, &input, r); // This is INPUT from the USER // ZF_LOGI_MEM( input, strlen(input), "<< "); } } // Restore terminal tcsetattr(1, TCSAFLUSH, &orig1); ZF_LOGD("exit"); } return 0; }