README000644 000423 000000 00000004161 11464704262 012204 0ustar00luigiwheel000000 000000 kiterm -- browser-based terminal for the kindle This package is a simplified and stripped down port of the AjaxTerm program that is suitable for operation on the Kindle. AjaxTerm uses some simple javascript on the client to access a server in charge of forking shells (and keeping them alive) and pass tty data across the HTTP connection. This approach is especially useful on the kindle, where running an xterm or a shell in console mode is challenging. Instead, the browser can be used for this. The original AjaxTerm uses a Python script, with the usual amount of classes, to implement its services. I have completely reimplemented the program in C, using a single process that handles multiple connections and forks the child shells as desired. The javascript has also been heavily simplified, removing unnecessary libraries. The tarball includes: + this README + myts.c (source code for the server) + myts.arm -- a version of the server compiled for the kindle + ajaxterm.{html,js,css}, html files To use the program you must run the server from the directory where the javascript files are, and then launch a browser to http://localhost:8022/ On the kindle, ALT maps to CTRL, and the ESC key is the "page back" key on the left of the screen. TODO: At this stage the program is still a bit experimental in that it still needs work on two areas, namely process management and terminal emulation. In particular: + complete ANSI control code emulation not all ANSI sequences are recognised, though most things work (top, vi, ...). The missing sequences mostly refer to color handling. + reconnect to existing sessions at the moment each session uses a random key so if the browser dies or disconnects, you cannot reach the existing session anymore + session termination shells remain alive until the server terminates. I should implement a way to explicitly kill a session or connect to one of the existing ones. === References === http://antony.lesuisse.org/software/ajaxterm/ wget http://antony.lesuisse.org/ajaxterm/files/Ajaxterm-0.10.tar.gz tar zxvf Ajaxterm-0.10.tar.gz cd Ajaxterm-0.10 ./ajaxterm.py ajaxterm.css000644 000423 000000 00000003242 11464701237 013647 0ustar00luigiwheel000000 000000 /* * $Id: ajaxterm.css 7656 2010-11-05 04:21:40Z luigi $ * default colors for kindle */ .kindle { background-color: white; color: black; } pre.stat { margin: 0px; padding: 4px; display: block; font-family: monospace; white-space: pre; background-color: black; border-top: 1px solid black; color: white; } pre.stat span { padding: 0px; } pre.stat .on { background-color: #080; font-weight: bold; color: white; cursor: pointer; } pre.stat .off { background-color: #888; font-weight: bold; color: white; cursor: pointer; } pre.term { margin: 0px; padding: 4px; display: block; font-family: monospace; white-space: pre; background-color: #fff; border-top: 1px solid white; color: #000; } pre.term span.f0 { color: #000; } pre.term span.f1 { color: #b00; } pre.term span.f2 { color: #0b0; } pre.term span.f3 { color: #bb0; } pre.term span.f4 { color: #00b; } pre.term span.f5 { color: #b0b; } pre.term span.f6 { color: #0bb; } pre.term span.f7 { color: #bbb; } pre.term span.f8 { color: #666; } pre.term span.f9 { color: #f00; } pre.term span.f10 { color: #0f0; } pre.term span.f11 { color: #ff0; } pre.term span.f12 { color: #00f; } pre.term span.f13 { color: #f0f; } pre.term span.f14 { color: #0ff; } pre.term span.f15 { color: #fff; } pre.term span.b0 { background-color: #000; } pre.term span.b1 { background-color: #b00; } pre.term span.b2 { background-color: #0b0; } pre.term span.b3 { background-color: #bb0; } pre.term span.b4 { background-color: #00b; } pre.term span.b5 { background-color: #b0b; } pre.term span.b6 { background-color: #0bb; } pre.term span.b7 { background-color: #bbb; } body { background-color: #888; } #term { float: left; } ajaxterm.html000644 000423 000000 00000001075 11464703015 014021 0ustar00luigiwheel000000 000000 Ajaxterm - Luigi Rizzo version
ajaxterm.js000644 000423 000000 00000027264 11464703243 013504 0ustar00luigiwheel000000 000000 /* * $Id: ajaxterm.js 7657 2010-11-05 04:38:48Z luigi $ * * setHTML works around an IE bug that requires content to be installed twice * (and still without working handlers) */ function setHTML(el, t) { if (!el) return; el.innerHTML = t; el.innerHTML = el.innerHTML; } ajaxterm={}; ajaxterm.Terminal_ctor=function(id,width,height, keylen) { if (!width) width=80; if (!height) height=25; if (!keylen) keylen=16; var ie=(window.ActiveXObject) ? 1 : 0; var webkit= (navigator.userAgent.indexOf("WebKit") >= 0) ? 1 : 0; var sid=""; var alphabet = 'abcdefghijklmnopqrstuvwxyz'; var l = alphabet.length; for (var i=0; i < keylen; i++) { // generate a session key sid += alphabet.charAt(Math.round(Math.random()*l)); } var query0="s="+sid+"&w="+width+"&h="+height; var query1=query0+"&c=1&k="; var buf=""; var timeout; var error_timeout; var keybuf=[]; var sending=0; var rmax=1; /* elements in the top bar */ var div=document.getElementById(id); var dstat=document.createElement('pre'); var opt_get=document.createElement('a'); var opt_color=document.createElement('a'); var opt_paste=document.createElement('a'); var sdebug=document.createElement('span'); var dterm=document.createElement('div'); function debug(s) { setHTML(sdebug, s); } function error() { debug("Connection lost timeout ts:"+((new Date).getTime())); } function opt_add(opt,name) { opt.className='off'; setHTML(opt, ' '+name+' '); dstat.appendChild(opt); dstat.appendChild(document.createTextNode(' ')); } function do_get(event) { /* toggle get/post */ opt_get.className=(opt_get.className=='off')?'on':'off'; debug('GET '+opt_get.className); } function do_color(event) { var o=opt_color.className=(opt_color.className=='off')?'on':'off'; query1 = query0 + (o=='on' ? "&c=1" : "") + "&k="; debug('Color '+opt_color.className); } function mozilla_clipboard() { // mozilla sucks try { netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); } catch (err) { debug('Access denied, more info'); return undefined; } var clip = Components.classes["@mozilla.org/widget/clipboard;1"].createInstance(Components.interfaces.nsIClipboard); var trans = Components.classes["@mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable); if (!clip || !trans) { return undefined; } trans.addDataFlavor("text/unicode"); clip.getData(trans,clip.kGlobalClipboard); var str=new Object(); var strLength=new Object(); try { trans.getTransferData("text/unicode",str,strLength); } catch(err) { return ""; } if (str) { str=str.value.QueryInterface(Components.interfaces.nsISupportsString); } return (str) ? str.data.substring(0,strLength.value / 2) : ""; } function do_paste(event) { /* not always working */ var p=undefined; if (window.clipboardData) { p=window.clipboardData.getData("Text"); } else if(window.netscape) { p=mozilla_clipboard(); } else { return; // failed } debug('Pasted'); queue(encodeURIComponent(p)); } function update() { if (sending) return; sending=1; var r=new XMLHttpRequest(); var send=""; while (keybuf.length>0) { send+=keybuf.pop(); } var query=query1+send; if (opt_get.className=='on') { r.open("GET","u?"+query,true); if (ie) { // force a refresh r.setRequestHeader("If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT"); } } else { r.open("POST","u",true); } r.setRequestHeader('Content-Type','application/x-www-form-urlencoded'); r.onreadystatechange = function () { if (r.readyState!=4) return; window.clearTimeout(error_timeout); if (r.status!=200) { debug("Connection error, status: "+r.status + ' ' + r.statusText); return; } if(ie) { var responseXMLdoc = new ActiveXObject("Microsoft.XMLDOM"); responseXMLdoc.loadXML(r.responseText); de = responseXMLdoc.documentElement; } else { de=r.responseXML.documentElement; } if (de.tagName=="pre") { setHTML(dterm, unescape(r.responseText)); rmax=100; } else { rmax*=2; if(rmax>2000) rmax=2000; } sending=0; timeout=window.setTimeout(update,rmax); } error_timeout=window.setTimeout(error,5000); r.send ( (opt_get.className=='on') ? null : query ); } function queue(s) { keybuf.unshift(s); if (sending==0) { window.clearTimeout(timeout); timeout=window.setTimeout(update,1); } } function keypress(ev) { if (!ev) var ev=window.event; s="kp kC="+ev.keyCode+" w="+ev.which + " sh="+ev.shiftKey+" ct="+ev.ctrlKey+" al="+ev.altKey; debug(s); var kc; var k=""; if (ev.keyCode) kc=ev.keyCode; if (ev.which) kc=ev.which; if (0 && ev.altKey) { /* ESC + char */ if (kc>=65 && kc<=90) kc+=32; if (kc>=97 && kc<=122) k=String.fromCharCode(27)+String.fromCharCode(kc); } else if (ev.ctrlKey || ev.altKey) { if (kc>=65 && kc<=90) k=String.fromCharCode(kc-64); // Ctrl-A..Z else if (kc>=97 && kc<=122) k=String.fromCharCode(kc-96); // Ctrl-A..Z else if (kc==54) k=String.fromCharCode(30); // Ctrl-^ else if (kc==109) k=String.fromCharCode(31); // Ctrl-_ else if (kc==219) k=String.fromCharCode(27); // Ctrl-[ else if (kc==220) k=String.fromCharCode(28); // Ctrl-\ else if (kc==221) k=String.fromCharCode(29); // Ctrl-] else if (kc==219) k=String.fromCharCode(29); // Ctrl-] else if (kc==219) k=String.fromCharCode(0); // Ctrl-@ } else if (ev.which==0) { if (kc==9) k=String.fromCharCode(9); // Tab else if (kc==8) k=String.fromCharCode(127); // Backspace else if (kc==27) k=String.fromCharCode(27); // Escape else { if (kc==33) k="[5~"; // PgUp else if (kc==34) k="[6~"; // PgDn else if (kc==35) k="[4~"; // End else if (kc==36) k="[1~"; // Home else if (kc==37) k="[D"; // Left else if (kc==38) k="[A"; // Up else if (kc==39) k="[C"; // Right else if (kc==40) k="[B"; // Down else if (kc==45) k="[2~"; // Ins else if (kc==46) k="[3~"; // Del else if (kc==112) k="[[A"; // F1 else if (kc==113) k="[[B"; // F2 else if (kc==114) k="[[C"; // F3 else if (kc==115) k="[[D"; // F4 else if (kc==116) k="[[E"; // F5 else if (kc==117) k="[17~"; // F6 else if (kc==118) k="[18~"; // F7 else if (kc==119) k="[19~"; // F8 else if (kc==120) k="[20~"; // F9 else if (kc==121) k="[21~"; // F10 else if (kc==122) k="[23~"; // F11 else if (kc==123) k="[24~"; // F12 if (k.length) k=String.fromCharCode(27)+k; } } else { if (kc==8) k=String.fromCharCode(127); // Backspace else k=String.fromCharCode(kc); } if (k.length) queue( (k=="+") ? "%2B" : utf8Escape(k) ); ev.cancelBubble=true; if (ev.stopPropagation) ev.stopPropagation(); if (ev.preventDefault) ev.preventDefault(); return false; } function keydown(ev) { if (!ev) var ev=window.event; if (ie || webkit) { s="kd kC="+ev.keyCode+" w="+ev.which+" sh=" + ev.shiftKey+" ct="+ev.ctrlKey+" al="+ev.altKey; debug(s); o={9:1,8:1,27:1,33:1,34:1,35:1,36:1,37:1,38:1,39:1,40:1,45:1,46:1,112:1, 113:1,114:1,115:1,116:1,117:1,118:1,119:1,120:1,121:1,122:1,123:1}; if (o[ev.keyCode] || ev.ctrlKey || ev.altKey) { ev.which=0; return keypress(ev); } } } function keyup(ev) { if (!ev) var ev=window.event; if (ie || webkit) { s="ku kC="+ev.keyCode+" w="+ev.which+" sh=" + ev.shiftKey+" ct="+ev.ctrlKey+" al="+ev.altKey; debug(s); if (ev.keyCode == 33) queue("%1B"); return false; o={9:1,8:1,27:1,33:1,34:1,35:1,36:1,37:1,38:1,39:1,40:1,45:1,46:1,112:1, 113:1,114:1,115:1,116:1,117:1,118:1,119:1,120:1,121:1,122:1,123:1}; return; if (o[ev.keyCode] || ev.ctrlKey || ev.altKey) { ev.which=0; return keypress(ev); } } } function init() { dstat.appendChild(document.createTextNode(' ')); opt_add(opt_color,'Colors'); opt_color.className='on'; opt_color.title='Toggle color or grey'; opt_add(opt_get,'GET'); opt_get.title = 'Toggle GET or POST methods'; opt_add(opt_paste,'Paste'); opt_paste.title = 'Paste from clipboard'; dstat.appendChild(sdebug); dstat.className='stat'; div.appendChild(dstat); div.appendChild(dterm); if(opt_color.addEventListener) { opt_get.addEventListener('click',do_get,true); opt_color.addEventListener('click',do_color,true); opt_paste.addEventListener('click',do_paste,true); } else { opt_get.attachEvent("onclick", do_get); opt_color.attachEvent("onclick", do_color); opt_paste.attachEvent("onclick", do_paste); } document.onkeypress=keypress; document.onkeydown=keydown; document.onkeyup=keyup; timeout=window.setTimeout(update,100); } init(); debug('Session: ' + sid); } ajaxterm.Terminal=function(id,width,height, keylen) { return new this.Terminal_ctor(id,width,height, keylen); } /* escape to utf8 -- note that this is similar to encodeURIComponent */ var char2hex = new Array( "%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07", "%08", "%09", "%0a", "%0b", "%0c", "%0d", "%0e", "%0f", "%10", "%11", "%12", "%13", "%14", "%15", "%16", "%17", "%18", "%19", "%1a", "%1b", "%1c", "%1d", "%1e", "%1f", "%20", "%21", "%22", "%23", "%24", "%25", "%26", "%27", "%28", "%29", "%2a", "%2b", "%2c", "%2d", "%2e", "%2f", "%30", "%31", "%32", "%33", "%34", "%35", "%36", "%37", "%38", "%39", "%3a", "%3b", "%3c", "%3d", "%3e", "%3f", "%40", "%41", "%42", "%43", "%44", "%45", "%46", "%47", "%48", "%49", "%4a", "%4b", "%4c", "%4d", "%4e", "%4f", "%50", "%51", "%52", "%53", "%54", "%55", "%56", "%57", "%58", "%59", "%5a", "%5b", "%5c", "%5d", "%5e", "%5f", "%60", "%61", "%62", "%63", "%64", "%65", "%66", "%67", "%68", "%69", "%6a", "%6b", "%6c", "%6d", "%6e", "%6f", "%70", "%71", "%72", "%73", "%74", "%75", "%76", "%77", "%78", "%79", "%7a", "%7b", "%7c", "%7d", "%7e", "%7f", "%80", "%81", "%82", "%83", "%84", "%85", "%86", "%87", "%88", "%89", "%8a", "%8b", "%8c", "%8d", "%8e", "%8f", "%90", "%91", "%92", "%93", "%94", "%95", "%96", "%97", "%98", "%99", "%9a", "%9b", "%9c", "%9d", "%9e", "%9f", "%a0", "%a1", "%a2", "%a3", "%a4", "%a5", "%a6", "%a7", "%a8", "%a9", "%aa", "%ab", "%ac", "%ad", "%ae", "%af", "%b0", "%b1", "%b2", "%b3", "%b4", "%b5", "%b6", "%b7", "%b8", "%b9", "%ba", "%bb", "%bc", "%bd", "%be", "%bf", "%c0", "%c1", "%c2", "%c3", "%c4", "%c5", "%c6", "%c7", "%c8", "%c9", "%ca", "%cb", "%cc", "%cd", "%ce", "%cf", "%d0", "%d1", "%d2", "%d3", "%d4", "%d5", "%d6", "%d7", "%d8", "%d9", "%da", "%db", "%dc", "%dd", "%de", "%df", "%e0", "%e1", "%e2", "%e3", "%e4", "%e5", "%e6", "%e7", "%e8", "%e9", "%ea", "%eb", "%ec", "%ed", "%ee", "%ef", "%f0", "%f1", "%f2", "%f3", "%f4", "%f5", "%f6", "%f7", "%f8", "%f9", "%fa", "%fb", "%fc", "%fd", "%fe", "%ff" ); function utf8Escape(s) { var ret = "", i, len = s.length; for (i = 0; i < len; i++) { var ch = s.charCodeAt(i); if ( (65 <= ch && ch <= 90) || (97 <= ch && ch <= 122) || (48 <= ch && ch <= 57) || ch == 45 || ch == 95 || ch == 46 || ch == 33 || ch == 126 || ch == 42 || ch == 39 || ch == 40 || ch == 41) { ret += String.fromCharCode(ch); } else if (ch == 32) { ret += '+'; } else if (ch <= 0x007F) { ret += char2hex[ch]; } else if (ch <= 0x07FF) { ret += char2hex[0xc0 | (ch >> 6)]; ret += char2hex[0x80 | (ch & 0x3F)]; } else { ret += char2hex[0xe0 | (ch >> 12)]; ret += char2hex[0x80 | ((ch >> 6) & 0x3F)]; ret += char2hex[0x80 | (ch & 0x3F)]; } } return ret; } myts.c000644 000423 000000 00000053476 11464703243 012477 0ustar00luigiwheel000000 000000 /* Copyright (c) 2010 Luigi Rizzo. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * $Id: myts.c 7657 2010-11-05 04:38:48Z luigi $ Backend for ajaxterm The main instance of a program keeps a list of current sessions, and for each of them handles a child which runs a shell and talks through a pty pair. In standalone mode, the process runs as a web server and so it can suspend requests for some time until they are handled. In slave1 mode, it talks through a single unix pipe so it cannot suspend. In slave2 mode it uses multiple unix pipes. */ #include #include #include #include #include #include #include #include #include #include #ifdef linux #include /* strcasestr */ /* strcasestr prototype is problematic */ char *strcasestr(const char *haystack, const char *pneedle); #include #else #include /* forkpty */ #endif #include /* gettimeofday */ #include #include #include /* PROT_READ and mmap */ #include #include /* gethostbyname */ #include /* isalnum */ #include /* inet_aton */ #define KMAX 256 /* keyboard queue */ #define SMAX 256 /* keyboard queue */ #define ROWS 25 #define COLS 80 #define INBUFSZ 4096 /* GET/POST queries */ #define OUTBUFSZ (5*ROWS*COLS) /* output buffer */ /* supported mime types -- suffix space mime. Default is text/plain. * processed by getmime(filename) */ static const char *mime_types[] = { "html text/html", "htm text/html", "js text/javascript", "css text/css", NULL }; /* * struct my_sock contains support for talking to the browser. * It contains a buffer for receiving the incoming request, * hold a copy of the response, and support for an mmapped file. * Initially, reply = 0, len = sizeof(inbuf) and pos = 0, * we accumulate data into inbuf and call parse_msg() to tell * whether we are done. * During the reply phase, inbuf contains the response hdr, * possibly map contains the body, len = header length, body_len = body_len * and pos = 0. We first send the header, then toggle len = -body_len, * and then send the body. When done, detach the buffer. * If filep is set, then map is a mapped file, otherwise points to * some other buffer and does not need to be freed. */ struct my_sock { struct my_sock *next; int socket; /* the network socket */ int reply; /* set when replying */ int pos, len; /* read position and length */ struct sockaddr_in sa; /* not used */ char inbuf[INBUFSZ]; /* I/O buffer */ char outbuf[OUTBUFSZ]; /* I/O buffer */ /* memory mapped file */ int filep; int body_len; char *map; }; /* * struct my_sess describes a shell session to which we talk. */ struct my_sess { struct my_sess *next; char *name; /* session name */ int pid; /* pid of the child */ int master; /* master tty */ /* screen/keyboard buf have len *pos. *pos is the next byte to send */ int kseq; // need a sequence number for kb input ? int klen; /* pending input for keyboard */ char keys[KMAX]; int slen; /* pending input for screen */ char sbuf[SMAX]; int rows, cols; /* geometry */ int cur; char *page; /* dump of the screen */ char *oldpage; /* dump of the screen */ }; /* * my_args contains all the arguments for the program */ struct my_args { struct sockaddr_in sa; char *cmd; /* command to run */ int nsess; /* number of sessions */ int lfd; /* listener fd */ struct my_sock *socks; struct my_sess *sess; int cycles; int unsafe; /* allow read all file systems */ int verbose; /* allow read all file systems */ }; int myerr(const char *s) { fprintf(stderr, "error: %s\n", s); exit(2); } /* convert the html encoding back to plain ascii * XXX must be fixed to handle UTF8 */ char *unescape(char *s) { char *src, *dst, c, *hex = "0123456789abcdef0123456789ABCDEF"; for (src = dst = s; *src; ) { c = *src++; if (c == '+') c = ' '; else if (c == '%') { c = '\0'; if (*src && index(hex, *src)) c = c*16 + ((index(hex, *src++)-hex)&0xf); if (*src && index(hex, *src)) c = c*16 + ((index(hex, *src++)-hex)&0xf); } *dst++ = c; } *dst++ = '\0'; return s; } /* * append a string to a page, interpreting ANSI sequences */ char *page_append(struct my_sess *sh, char *s) { int pagelen = sh->rows * sh->cols; int curcol; for (; *s; s++) { char c = *s; if (sh->cur >= pagelen) { // fprintf(stderr, "+++ scroll at %d / %d +++\n", sh->cur, pagelen); sh->cur = pagelen - sh->cols; memcpy(sh->page, sh->page + sh->cols, sh->cur); memset(sh->page + pagelen - sh->cols, ' ', sh->cols); } curcol = sh->cur % sh->cols; /* now should map actions */ if (c == '\r') { sh->cur -= curcol; } else if (c == '\n') { sh->cur += sh->cols; if (sh->cur >= pagelen) { // XXX not sure if needed // fprintf(stderr, "+++ scroll2 at %d / %d +++\n", sh->cur, pagelen); sh->cur -= sh->cols; memcpy(sh->page, sh->page + sh->cols, sh->cur); memset(sh->page + pagelen - sh->cols, ' ', sh->cols); } } else if (c == '\t') { if (curcol >= pagelen - 8) sh->cur += (sh->cols - 1 - curcol); else sh->cur += 8 - (sh->cur % 8); } else if (c == '\b') { // backspace if (curcol > 0) sh->cur--; sh->page[sh->cur] = ' '; } else if (c == '\033') { /* escape */ if (!s[1]) break; // process later if (s[1] == '[' ) { // CSI found /* see http://en.wikipedia.org/wiki/ANSI_escape_code */ char *parm, *base = s + 2, cmd, mark=' '; int a1= 1, a2= 1, a3 = 1; // fprintf(stderr, "+++ CSI FOUND ESC-%s\n", s+1); if (*base == '?') mark = *base++; if (!*base) break; // process later // skip parameters for (parm = base; *parm && index("0123456789;", *parm); parm++) ; // fprintf(stderr, "+++ now PARM %s\n", parm); cmd = parm[0]; if (!cmd) return s; // process later s = parm; sscanf(base, "%d;%d;%d", &a1, &a2, &a3); if (cmd == 'A') { // up while (a1-- > 0) { if (sh->cur >= sh->cols) sh->cur -= sh->cols; } } else if (cmd == 'B') { // down while (a1-- > 0) { if (sh->cur < pagelen -sh->cols) sh->cur += sh->cols; } } else if (cmd == 'C') { // right if (a1 >= sh->cols - curcol) a1 = sh->cols - curcol - 1; sh->cur += a1; } else if (cmd == 'D') { // left if (a1 > curcol) a1 = curcol; sh->cur -= a1; } else if (cmd == 'H' || cmd == 'f') { // position if (a1 > sh->rows) a1 = sh->rows; if (a2 > sh->cols) a2 = sh->cols; sh->cur = (a1 - 1)*sh->cols + a2 - 1; } else if (cmd == 'J') { /* clear part of screen */ if (base == parm || a1 == 0) { a1 = pagelen - sh->cols; memset(sh->page + sh->cur, ' ', a1); } else if (a1 == 1) { memset(sh->page, ' ', sh->cur); } else if (a1 == 2) { memset(sh->page, ' ', pagelen); sh->cur = 0; // msdos ansy.sys } else { goto notfound; } } else if (cmd == 'K') { /* clear */ if (base == parm || a1 == 0) { a1 = sh->cols - curcol; memset(sh->page + sh->cur, ' ', a1); } else if (a1 == 1) { goto notfound; } else if (a1 == 2) { goto notfound; } else { goto notfound; } } else if (mark == '?' && cmd == 'l') { /* hide cursor */ goto notfound; } else if (mark == '?' && cmd == 'h') { /* show cursor */ goto notfound; } else { notfound: fprintf(stderr, "ANSI sequence %d %d %d ( ESC-[%c%.*s)\n", a1, a2, a3, mark, (parm+1 - base), base); } } } else { sh->page[sh->cur] = *s; if (curcol != sh->cols -1) sh->cur++; } if (sh->cur >= pagelen) fprintf(stderr,"--- ouch, overflow on c %d\n", c); } if (*s) { fprintf(stderr, "----- leftover stuff ESC [%s]\n", s+1); } return s; } int forkchild(struct my_sess *s, char *cmd) { struct winsize ws; bzero(&ws, sizeof(ws)); ws.ws_row = s->rows; ws.ws_col = s->cols; s->pid = forkpty(&s->master, NULL, NULL, &ws); // fprintf(stderr, "forkpty gives pid %d pty %d\n", s->pid, s->master); if (s->pid < 0) { fprintf(stderr, "forkpty failed\n"); return 1; } if (s->pid == 0) { /* execvp the shell */ char *av[] = { cmd, NULL}; execvp(av[0], av); exit(1); } return 0; } int u_mode(struct my_args *me, struct my_sock *ss, char *body) { /* ajaxterm parameters */ char *s = NULL, *w = NULL, *h = NULL, *c = NULL, *k = NULL; char *cur, *p, *p2; struct timeval t; char tmp[ROWS*COLS+1]; char *src, *dst; char done; int i, l, rows = 0, cols = 0; struct my_sess *sh = NULL; for (p = body; (cur = strsep(&p, "&")); ) { if (!*cur) continue; p2 = strsep(&cur, "="); if (!strcmp(p2, "s")) s = cur; if (!strcmp(p2, "w")) w = cur; if (!strcmp(p2, "h")) h = cur; if (!strcmp(p2, "c")) c = cur; if (!strcmp(p2, "k")) k = cur; } if (!s || !*s) goto error; if (w) cols = atoi(w); if (cols < 10 || cols > 150) cols = 80; if (h) rows = atoi(h); if (rows < 4 || rows > 80) rows = 25; for (i = 0, sh = me->sess; sh; sh = sh->next, i++) { // fprintf(stderr, "at %p have %s\n", sh, sh->name); if (!strcmp(s, sh->name)) break; } if (!sh) { // fprintf(stderr, "--- session %s not found %d\n", s, i); int l1 = rows * cols + 1; int l2 = strlen(s) + 1; int pagelen = rows*cols; sh = calloc(1, sizeof(*sh) + l1*2 + l2); if (!sh) goto error; sh->rows = rows; sh->cols = cols; sh->cur = 0; sh->name = (char *)(sh + 1); sh->page = sh->name + l2; sh->oldpage = sh->page + l1; memset(sh->page, ' ', pagelen); strcpy(sh->name, s); sh->next = me->sess; me->sess = sh; if (forkchild(sh, me->cmd)) { ss->len = sprintf(ss->outbuf, "HTTP/1.1 400 fork failed\r\n\r\n"); return 0; } } unescape(k); strncat(sh->keys + sh->klen, k, sizeof(sh->keys) - 1 - sh->klen); sh->klen = strlen(sh->keys); rows = sh->rows; cols = sh->cols; src = sh->page; sh->page[rows*cols] = '\0'; // ensure it is terminated XXX bug in cursor handling if (!strcmp(sh->page, sh->oldpage)) { /* no modifications, compact version */ same: ss->len = sprintf(ss->outbuf, "HTTP/1.1 200 OK\r\n" "Content-Type: text/xml\r\n\r\n" "" ""); if (me->verbose) fprintf(stderr, "response %s\n", ss->outbuf); return 0; } else { strcpy(sh->oldpage, sh->page); } goto good; error: goto same; rows = ROWS; cols = COLS; gettimeofday(&t, NULL); l = sprintf(tmp, "session %p at %d.%06d pressed %s", ss, (int)t.tv_sec, (int)(t.tv_usec), k); src = tmp; good: ss->len = sprintf(ss->outbuf, "HTTP/1.1 200 OK\r\n" "Content-Type: text/xml\r\n\r\n" "" "
");
	done = '\0';

	for (i=0, dst = ss->outbuf + ss->len; i < rows*cols;) {
	    char cc = done ? done : src[i];
	    if (!cc) done = cc = ' ';
	    if (sh && src == sh->page && i == sh->cur)
		dst += sprintf(dst, "");
	    if (isalnum(cc) || cc == ' ') // XXX
		    *dst++ = cc;
	    else
		dst += sprintf(dst, "%%%02x", cc);
	    if (sh && src == sh->page && i == sh->cur)
		dst += sprintf(dst, "");
	    if (++i % cols == 0)
		*dst++ = '\n';
	}
	l = sprintf(dst, "
"); ss->len = dst + l - ss->outbuf; if (me->verbose) fprintf(stderr, "response %s\n", ss->outbuf); return 0; } /* * HTTP support */ int opensock(struct sockaddr_in sa) { int fd; int i; fd = socket(sa.sin_family, SOCK_STREAM, 0); if (fd < 0) { perror(" cannot create socket"); return -1; } fcntl(fd, F_SETFD, 1 ); // close on exec i = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i) ) < 0 ) { perror(" cannot reuseaddr"); return -1; } if (bind(fd, (struct sockaddr *)&sa, sizeof(struct sockaddr_in))) { perror( "bind" ); return -1; } if (listen(fd, 20) < 0 ) { perror( "listen" ); return -1; } return fd; } int handle_listen(struct my_args *me) { int fd; unsigned int l; struct sockaddr_in sa; struct my_sock *s; if (me->verbose) fprintf(stderr, "listening socket\n"); bzero(&sa, sizeof(sa)); l = sizeof(sa); fd = accept(me->lfd, (struct sockaddr *)&sa, &l); if (fd < 0) { fprintf(stderr, "listen failed\n"); return -1; } s = calloc(1, sizeof(*s)); if (!s) { close(fd); fprintf(stderr, "alloc failed\n"); return -1; } s->sa = sa; s->socket = fd; s->filep = -1; /* no file */ s->pos = 0; s->len = sizeof(s->inbuf); s->next = me->socks; me->socks = s; return 0; } /* support function to return mime types. */ const char *getmime(const char *res) { const char **p, *suffix; int lres = strlen(res); res += lres; /* move to the end of the resource */ for (p = mime_types; (suffix = *p); p++) { int lsuff = strcspn(suffix, " "); if (lsuff > lres) continue; if (!bcmp(res - lsuff, suffix, lsuff)) return suffix + lsuff + 1; } return "text/plain"; /* default */ } /* * A stripped down parser for http, which also interprets what * we need to do. */ int parse_msg(struct my_args *me, struct my_sock *s) { char *a, *b, *c = s->inbuf; /* strsep support */ char *body, *method = NULL, *resource = NULL; int row, tok; int clen = -1; char *err = "generic error"; if (s->pos == s->len) { // buffer full, we are done fprintf(stderr, "--- XXX input buffer full\n"); s->len = 0; // complete } /* locate the end of the header. If not found, just return */ body = strstr(s->inbuf, "\n\r\n") + 3; if (body < s->inbuf) body = strstr(s->inbuf, "\n\n") + 2; if (body < s->inbuf && s->len) return 0; /* quick search for content length */ a = strcasestr(s->inbuf, "Content-length:"); if (a && a < body) { sscanf(a + strlen("Content-length:") + 1, "%d", &clen); if (me->verbose) fprintf(stderr, "content length = %d, body len %d\n", clen, s->pos - (body - s->inbuf)); if (s->pos - (body - s->inbuf) != clen) return 0; } /* no content length, hope body is complete */ /* XXX maybe do a multipass */ /* now parse the header */ for (row=0; (b = strsep(&c, "\r\n"));) { if (*b == '\0') continue; if (b > body) { body = b; break; } row++; for (tok=0; (a = strsep(&b, " \t"));) { if (*a == '\0') continue; tok++; if (row == 1) { if (tok == 1) method = a; if (tok == 2) resource = a; } } } s->reply = 1; /* body found, we move to reply mode. */ if (me->verbose) fprintf(stderr, "%s %s\n", method, resource); if (me->verbose) fprintf(stderr, "+++ request body [%s]\n", body); s->pos = 0; if (!strcmp(method, "POST") && !strcmp(resource, "/u")) { /* this is the ajax request */ return u_mode(me, s, body); } else if (!strcmp(method, "GET") && !strncmp(resource, "/u?", 3)) { /* same ajax request using GET */ return u_mode(me, s, resource+3); } else { /* request for a file, map and serve it */ struct stat sb; err = "invalid pathname"; if (!me->unsafe && resource[1] == '/') goto error; /* avoid absolute pathnames */ for (a = resource+1; *a; a++) { /* avoid back pointers */ if (*a == '.' && a[1] == '.') goto error; } if (!strcmp(resource, "/")) resource = "/ajaxterm.html"; s->filep = open(resource+1, O_RDONLY); err = "open failed"; if (s->filep < 0 || fstat(s->filep, &sb)) goto error; err = "mmap failed"; /* linux wants MAP_PRIVATE or MAP_SHARED, not 0 */ s->map = mmap(NULL, (int)sb.st_size, PROT_READ, MAP_PRIVATE, s->filep, (off_t)0); if (s->map == MAP_FAILED) goto error; s->body_len = sb.st_size; s->len = sprintf(s->outbuf, "HTTP/1.1 200 OK\r\n" "Content-Type: %s\r\nContent-Length: %d\r\n\r\n", getmime(resource+1), (int)sb.st_size); return 0; } error: if (s->filep >= 0) close(s->filep); s->len = sprintf(s->outbuf, "HTTP/1.1 200 OK\r\n" "Content-Type: text/plain\r\n\r\nResource %s : %s\n", resource, err); return 0; } /* * Handle I/O on the socket talking to the browser. * We always use it in half duplex */ int sock_io(struct my_args *me, struct my_sock *s) { int l; if (s->reply) { /* first write the header, then set s->len negative and * write the mapped file */ if (s->len > 0) { l = write(s->socket, s->outbuf + s->pos, s->len - s->pos); } else { l = write(s->socket, s->map + s->pos, s->body_len - s->pos); } if (l <= 0) goto write_done; s->pos += l; if (me->verbose) fprintf(stderr, "written1 %d/%d\n", s->pos, s->len); if (s->pos == s->len) { /* header sent, move to the body */ s->len = -s->body_len; s->pos = 0; } if (me->verbose) fprintf(stderr, "written2 %d/%d\n", s->pos, -s->len); if (s->pos == -s->len) { /* body sent, close */ write_done: if (me->verbose) fprintf(stderr, "reply complete\n"); /* the kindle wants shutdown before close */ shutdown(s->socket, SHUT_RDWR); close(s->socket); s->len = 0; if (s->filep) { if (s->map) munmap(s->map, s->body_len); close(s->filep); } } } else { /* accumulate request */ l = read(s->socket, s->inbuf + s->pos, s->len - s->pos); if (me->verbose) fprintf(stderr, "read %p returns %d %s\n", s, l, s->inbuf); if (l <= 0) { fprintf(stderr, "buf [%s]\n", s->inbuf); s->len = 0; // mark done with read } else { s->pos += l; } parse_msg(me, s); /* check if msg is complete */ } return 0; } int shell_keyboard(struct my_args *me, struct my_sess *sh) { int l; l = write(sh->master, sh->keys, sh->klen); if (l <= 0) return 1; strcpy(sh->keys, sh->keys + l); sh->klen -= l; return 0; } /* process screen output from the shell */ int shell_screen(struct my_args *me, struct my_sess *p) { int l, spos; char *s; spos = strlen(p->sbuf); l = read(p->master, p->sbuf + spos, sizeof(p->sbuf) - 1 - spos); if (l <= 0) { fprintf(stderr, "--- screen gives %d\n", l); p->master = -1; return 1; } spos += l; p->sbuf[spos] = '\0'; s = page_append(p, p->sbuf); /* returns unprocessed pointer */ strcpy(p->sbuf, s); return 0; } /* * Main loop implementing web server and connection handling */ int mainloop(struct my_args *me) { fprintf(stderr, "listen on %s:%d\n", inet_ntoa(me->sa.sin_addr), ntohs(me->sa.sin_port)); me->lfd = opensock(me->sa); for (;;) { int n, nmax; struct my_sock *s, *nexts, **ps; struct my_sess *p, *nextp, **pp; fd_set r, w; struct timeval to = { 5, 0 }; FD_ZERO(&r); FD_ZERO(&w); FD_SET(me->lfd, &r); nmax = me->lfd; for (s = me->socks; s; s = s->next) { /* handle sockets */ FD_SET(s->socket, s->reply ? &w : &r); if (nmax < s->socket) nmax = s->socket; } for (p = me->sess; p; p = p->next) { /* handle terminals */ FD_SET(p->master, &r); if (nmax < p->master) nmax = p->master; if (p->klen) /* have bytes to send to keyboard */ FD_SET(p->master, &w); } n = select(nmax + 1, &r, &w, NULL, &to); if (n == 0) { fprintf(stderr, "select returns %d\n", n); continue; } if (FD_ISSET(me->lfd, &r)) handle_listen(me); for (ps = &me->socks, s = *ps; s; s = nexts) { /* scan sockets */ nexts = s->next; if (FD_ISSET(s->socket, s->reply ? &w : &r)) sock_io(me, s); if (s->len != 0) { /* socket still active */ ps = &s->next; } else { /* socket dead, unlink */ *ps = s->next; free(s); } } for (pp = &me->sess, p = *pp; p ; p = nextp) { /* scan shells */ nextp = p->next; if (FD_ISSET(p->master, &w)) shell_keyboard(me, p); if (p->master >= 0 && FD_ISSET(p->master, &r)) shell_screen(me, p); if (p->master >= 0) { /* session still active */ pp = &p->next; } else { /* dead session, unlink */ *pp = p->next; fprintf(stderr, "-- free session %p ---\n", p); free(p); } } } return 0; } int main(int argc, char *argv[]) { struct my_args me; bzero(&me, sizeof(me)); me.sa.sin_family = PF_INET; me.sa.sin_port = htons(8022); inet_aton("127.0.0.1", &me.sa.sin_addr); me.cmd = "login"; for ( ; argc > 1 ; argc--, argv++) { if (!strcmp(argv[1], "--unsafe")) { me.unsafe = 1; continue; } if (!strcmp(argv[1], "--verbose")) { me.verbose = 1; continue; } if (argc < 3) break; if (!strcmp(argv[1], "--cmd")) { me.cmd = argv[2]; argc--; argv++; continue; } if (!strcmp(argv[1], "--port")) { me.sa.sin_port = htons(atoi(argv[2])); argc--; argv++; continue; } if (!strcmp(argv[1], "--addr")) { struct hostent *h = gethostbyname(argv[2]); if (h) { me.sa.sin_addr = *(struct in_addr *)(h->h_addr); } else if (!inet_aton(argv[1], &me.sa.sin_addr)) { perror("cannot parse address"); exit(1); } argc--; argv++; continue; } break; } mainloop(&me); return 0; } myts.arm000755 000423 000000 00000045121 11464701264 013024 0ustar00luigiwheel000000 000000 ELF(434 (p/444444//000 0 0 0HHH Qtd/lib/ld-linux.so.3GNU%1,.(')#"0&-* /  %   $+!OdĉHЉC܉|Tt  T $0*<{HTd`dCldcxdNq`0px̊8U؊ :1 P,tI8dBD<PP[\0jh1t1$i`(u4\ȋaԋ0libutil.so.1__gmon_start___Jv_RegisterClassesforkptylibc.so.6strcasestrsocketstrcpyexitsprintfstrncmpinet_atonperrorinet_ntoalistenselectabortstrtolmmapcallocstrlenmemsetstrstrbindreadshutdownmemcpysetsockoptraise__ctype_b_locsscanfstderrmunmapgethostbynameindexexecvp__fxstatstrncatfwritecloseopenstrchrfprintfacceptfcntlbcmpstrcmp__libc_start_mainfreeGLIBC_2.4 ii 9ii 11'1 11111 1$1 (1 ,1 01 41 81<1@1D1H1L1P1T1X1\1`1d1h1l1p1t1x1|11 1!1"1#1$1%1&1(1)1*1+1,1-1.1/10@--HƏ ʌHƏ ʌ@Ə ʌ8Ə ʌ0Ə ʌ(Ə ʌ Ə ʌƏ ʌƏ ʌƏ ʌƏ ʌƏ ʌƏ ʌƏ ʌƏ ʌƏ ʌƏ ʌƏ ʌƏ ʌƏ ʌƏ ʌƏ ʌƏ ʌƏ ʌƏ ʌƏ ʌƏ ʌxƏ ʌpƏ ʌhƏ ʌ`Ə ʌXƏ ʌPƏ ʌHƏ ʌ@Ə ʌ8Ə ʌ0Ə ʌ(Ə ʌ Ə ʌƏ ʌƏ ʌƏ ʌƏ ʌƏ ʌƏ ʌƏ ʌ$  --0-wpT X0 0Q/rԤ 0S00/1 @-0S0S3/0@- 0 1تp@-@P  g`Pp{00f0pG-@d0@T`p 0S SP PX e SP0@T 0S S 10@-M@,0S  00U00! 0 0 0 00"PP 00?P 3l X0000P 80  :000Ѝ0147@- M @P@#  0#  BP`@ P <@P@ Ѝ$<PX0@-M@P0    PL D00 PP00 zЍ0`1O-,M` "2 Z $"Yʤ4 0  a$"(2y " 0b( R$R BJp Z0a$2 Z $"Y4 0$"  a$"(2V " 0b( / Z0IQ00C0a$2/.000bc$ZQ0E$2($" 00Z 0S [SZ0C$0 000?S 00?00PU @PU @JP$" 2R0c$2$ 0B$0RʉBU $ " 0bQ0$2$ 0B$0RzCU  2g$0P0@$0$2$ 0$2mDU$0W$p$2$ 0b$2bfUHU"$0R$ " 0R A$00C "#$2NJU [ $0S  " b$ (2$ =S( $"5S(  0$2+KU[ $0S  " g$ (2$ w0000000k0 $ 0 ("00 20CS$20$2$2Yp0| Z0S @0P,Ѝ@00 0$ 0ZAUTM1pܫp@-PGoG@ G d2 PL0Hk0 0p00F?@@;p1$O-p@T`& P`㘀 P+T @ %TQ MP 0i@Q DP"P0i0 @ @@@T00<O-$M P&`p!T =P0p0 sR0SP wR0S hR0S kR0S@T P0p0@0S 0So X  p 0@SPpZ  `0@LS` @T P6 @T 0 Fi@P? b r0$2#( 0,2  ] 00 @P ʋ$p2@ P000 4   biU 20(2 0 0(b,RPJ$@O EN00 ,0Sd 1 d^?:$00l /k00B}pY=`  XPPU PT (2 S$2S p7 0010 U0P JpT (2 S$2S p`Q 00Yx 0k:C0C0 ,0S 400 $Ѝ00U`1ЬL`hpG-pMp$@h@0d0 0Rx p4000\PTHPT0S(mPUBd ,0S $0 0#d 0e$0 00ed S*UL:\^` ^lQ 0S S S   S S 00S 0l00S `h@T 0S S S   S S 00S0h0l@0S 00,0S X2h 0X,0S 42H P0 04cP 0/S 0uS0S  m0GS0ES0TS 0S  :P  V(0S0/S@ 0S  .S0.S`A7 0 S0/S <1R@ 1PA' PA0004 00p@ 40 0N  $40|@0P $| 0pЍ0l0@cx1ԭܭ47$<7087<xp@-M`@0SW 0S $ bf 10C   l\P" 0 ,0S 1|0 0Rd100c00 0,0S 04!8 0cw 0s@,0S  00dR\0000S, 0P 0C^0L! 0 $ cP,0S d0$00l 0AU 00 000D$ 700>Ѝp<7187Ȯخ47O-IM43@ 3 4,4  `㘰  F?B0`0S0`0S2N!02<0Q @0S 0  0 !# !0PQ \  2N!0210 P0S 2!1210\F?0  0P1  21 0S2 @@T pP0S   2 1S2 0Sp00KU@ @T| pP 21 1S2  R21 0S2  0Sp008 D _UP @0  l1 Ъ 4O-4Mp`0 0 0 0 0 0 0 0$ 0( 0, 0 L10H10PI<88P@<P(P8  5P,P1 W3 ,P00pG' $P   4 <0pGP P 0000PPpGpGW`¹ 4ЍVLT`lt|Q  aB Qp 00`BSoq /oo @ r S?C S?C S>C S>C S=C  S=C Sresponse %s HTTP/1.1 200 OK Content-Type: text/xml
%%%02x
--- XXX input buffer full Content-length:%dcontent length = %d, body len %d %s %s +++ request body [%s] POST/u?invalid pathname/ajaxterm.htmlopen failedmmap failedHTTP/1.1 200 OK Content-Type: %s Content-Length: %d HTTP/1.1 200 OK Content-Type: text/plain Resource %s : %s written1 %d/%d written2 %d/%d reply complete read %p returns %d %s buf [%s] listen on %s:%d select returns %d -- free session %p --- login--unsafe--verbose--cmd--port--addrcannot parse addresshtml text/htmlhtm text/htmljs text/javascriptcss text/cssX<9 Ī00h؅Ȃ  0p(o؇oov 0ЯGCC: (Sourcery G++ Lite 2008q3-72) 4.3.2 |   | T XlD A.aeabi$ARM10TDMI .symtab.strtab.shstrtab.interp.note.ABI-tag.hash.dynsym.dynstr.gnu.version.gnu.version_r.rel.dyn.rel.plt.init.text.fini.rodata.ARM.exidx.eh_frame.init_array.fini_array.jcr.dynamic.got.data.bss.comment.debug_frame.ARM.attributes44#HH 1hh`7 Ȃ?؅GovvbTo؇@c l ((p u p <{ Ī*̪*p/ /000000 0 00011111* 2dpp2/2P8 f DQ4HhȂ؅v؇(    Ī ̪000 0011  4  Ī H 1 ̪ Ȫ )06< < T L1[00X X  00     Ў   ȏ ` h H h     h  <       ЪЪ11 D T  000+0> 0   G1 RdbĉHrЉT  ܉ T L t  &Ī , >P$bD" p0<Hh ` ̪Td`dld14 xd"10BWD" e`w0  x1̊8؊ 1 Xl 1(9( ?Rfv P,t8dD<ȏ PP1\01 $  h '1, 5( ChT1ftx 1`(  4#ȋ3ԋ0F  K call_gmon_start$a$dinit.ccrtstuff.c__JCR_LIST____do_global_dtors_auxcompleted.5877__do_global_dtors_aux_fini_array_entryframe_dummy__frame_dummy_init_array_entrymyts.cC.113.5502mime_types.divsi3_nodiv0shiftelf-init.c__FRAME_END____JCR_END___GLOBAL_OFFSET_TABLE___init_array_end__init_array_start_DYNAMICdata_startopen@@GLIBC_2.4mmap@@GLIBC_2.4abort@@GLIBC_2.4__libc_csu_finiparse_msgstrcasestr@@GLIBC_2.4_start__libc_start_main@@GLIBC_2.4getmimeshell_keyboardinet_ntoa@@GLIBC_2.4__gmon_start___Jv_RegisterClasses_finistrchr@@GLIBC_2.4listen@@GLIBC_2.4calloc@@GLIBC_2.4__aeabi_idiv0memset@@GLIBC_2.4execvp@@GLIBC_2.4perror@@GLIBC_2.4page_appendmainloop_IO_stdin_usedfree@@GLIBC_2.4read@@GLIBC_2.4write@@GLIBC_2.4__data_startsock_ioaccept@@GLIBC_2.4__bss_start__socket@@GLIBC_2.4inet_aton@@GLIBC_2.4__aeabi_ldiv0strlen@@GLIBC_2.4sscanf@@GLIBC_2.4__exidx_endmemcpy@@GLIBC_2.4__divsi3bcmp@@GLIBC_2.4__dso_handlestrtol@@GLIBC_2.4strcpy@@GLIBC_2.4__end__strncat@@GLIBC_2.4__libc_csu_init__bss_end__raise@@GLIBC_2.4myerrforkpty@@GLIBC_2.4shutdown@@GLIBC_2.4bind@@GLIBC_2.4strstr@@GLIBC_2.4select@@GLIBC_2.4close@@GLIBC_2.4fwrite@@GLIBC_2.4forkchild__ctype_b_loc@@GLIBC_2.4__bss_startfprintf@@GLIBC_2.4_bss_end____aeabi_idivmodshell_screen_endunescapehandle_listenfcntl@@GLIBC_2.4stderr@@GLIBC_2.4munmap@@GLIBC_2.4__fxstat@@GLIBC_2.4u_modeindex@@GLIBC_2.4_edatastrncmp@@GLIBC_2.4gethostbyname@@GLIBC_2.4__exidx_startsetsockopt@@GLIBC_2.4__aeabi_idivopensockstrcmp@@GLIBC_2.4exit@@GLIBC_2.4sprintf@@GLIBC_2.4main_init