]>
git.saurik.com Git - cycript.git/blob - Console.cpp
e43fab3abd5495e4463403b547f2b75180d88f5c
   1 /* Cycript - Remove Execution Server and Disassembler 
   2  * Copyright (C) 2009  Jay Freeman (saurik) 
   5 /* Modified BSD License {{{ */ 
   7  *        Redistribution and use in source and binary 
   8  * forms, with or without modification, are permitted 
   9  * provided that the following conditions are met: 
  11  * 1. Redistributions of source code must retain the 
  12  *    above copyright notice, this list of conditions 
  13  *    and the following disclaimer. 
  14  * 2. Redistributions in binary form must reproduce the 
  15  *    above copyright notice, this list of conditions 
  16  *    and the following disclaimer in the documentation 
  17  *    and/or other materials provided with the 
  19  * 3. The name of the author may not be used to endorse 
  20  *    or promote products derived from this software 
  21  *    without specific prior written permission. 
  23  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' 
  24  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 
  25  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
  26  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
  27  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE 
  28  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
  29  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
  30  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
  31  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
  32  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
  33  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 
  34  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
  35  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
  36  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
  40 #include "cycript.hpp" 
  47 #include <readline/readline.h> 
  48 #include <readline/history.h> 
  55 #include <sys/types.h> 
  59 #include "Cycript.tab.hh" 
  61 #include <sys/types.h> 
  62 #include <sys/socket.h> 
  63 #include <netinet/in.h> 
  67 #include <apr_getopt.h> 
  71 static volatile enum { 
  79 static jmp_buf ctrlc_
; 
  81 static void sigint(int) { 
 102 void Setup(CYDriver 
&driver
, cy::parser 
&parser
) { 
 105         parser
.set_debug_level(1); 
 108         driver
.strict_ 
= true; 
 111 void Setup(CYOutput 
&out
, CYDriver 
&driver
) { 
 112     out
.pretty_ 
= pretty_
; 
 114     CYContext 
context(driver
.pool_
); 
 115     driver
.program_
->Replace(context
); 
 118 void Run(int client
, const char *data
, size_t size
, FILE *fout 
= NULL
, bool expand 
= false) { 
 125         json 
= CYExecute(pool
, data
); 
 134         CYSendAll(client
, &size
, sizeof(size
)); 
 135         CYSendAll(client
, data
, size
); 
 137         CYRecvAll(client
, &size
, sizeof(size
)); 
 138         if (size 
== _not(size_t)) 
 141             char *temp(new(pool
) char[size 
+ 1]); 
 142             CYRecvAll(client
, temp
, size
); 
 149     if (json 
!= NULL 
&& fout 
!= NULL
) { 
 150         if (!expand 
|| json
[0] != '"' && json
[0] != '\'') 
 152         else for (size_t i(0); i 
!= size
; ++i
) 
 154                 fputc(json
[i
], fout
); 
 155             else switch(json
[++i
]) { 
 156                 case '\0': goto done
; 
 157                 case '\\': fputc('\\', fout
); break; 
 158                 case '\'': fputc('\'', fout
); break; 
 159                 case '"': fputc('"', fout
); break; 
 160                 case 'b': fputc('\b', fout
); break; 
 161                 case 'f': fputc('\f', fout
); break; 
 162                 case 'n': fputc('\n', fout
); break; 
 163                 case 'r': fputc('\r', fout
); break; 
 164                 case 't': fputc('\t', fout
); break; 
 165                 case 'v': fputc('\v', fout
); break; 
 166                 default: fputc('\\', fout
); --i
; break; 
 175 void Run(int client
, std::string 
&code
, FILE *fout 
= NULL
, bool expand 
= false) { 
 176     Run(client
, code
.c_str(), code
.size(), fout
, expand
); 
 179 int (*append_history$
)(int, const char *); 
 181 static void Console(apr_pool_t 
*pool
, int client
) { 
 183     if (const char *username 
= getenv("LOGNAME")) 
 184         passwd 
= getpwnam(username
); 
 186         passwd 
= getpwuid(getuid()); 
 188     const char *basedir(apr_psprintf(pool
, "%s/.cycript", passwd
->pw_dir
)); 
 189     const char *histfile(apr_psprintf(pool
, "%s/history", basedir
)); 
 192     mkdir(basedir
, 0700); 
 193     read_history(histfile
); 
 201     rl_bind_key('\t', rl_insert
); 
 203     struct sigaction action
; 
 204     sigemptyset(&action
.sa_mask
); 
 205     action
.sa_handler 
= &sigint
; 
 207     sigaction(SIGINT
, &action
, NULL
); 
 211         std::vector
<std::string
> lines
; 
 214         const char *prompt("cy# "); 
 216         if (setjmp(ctrlc_
) != 0) { 
 225         char *line(readline(prompt
)); 
 232             if (line
[0] == '?') { 
 233                 std::string 
data(line 
+ 1); 
 234                 if (data 
== "bypass") { 
 236                     fprintf(fout
, "bypass == %s\n", bypass 
? "true" : "false"); 
 238                 } else if (data 
== "debug") { 
 240                     fprintf(fout
, "debug == %s\n", debug 
? "true" : "false"); 
 242                 } else if (data 
== "expand") { 
 244                     fprintf(fout
, "expand == %s\n", expand 
? "true" : "false"); 
 255         char *begin(line
), *end(line 
+ strlen(line
)); 
 256         while (char *nl 
= reinterpret_cast<char *>(memchr(begin
, '\n', end 
- begin
))) { 
 258             lines
.push_back(begin
); 
 262         lines
.push_back(begin
); 
 272             cy::parser 
parser(driver
); 
 273             Setup(driver
, parser
); 
 275             driver
.data_ 
= command
.c_str(); 
 276             driver
.size_ 
= command
.size(); 
 278             if (parser
.parse() != 0 || !driver
.errors_
.empty()) { 
 279                 for (CYDriver::Errors::const_iterator 
error(driver
.errors_
.begin()); error 
!= driver
.errors_
.end(); ++error
) { 
 280                     cy::position 
begin(error
->location_
.begin
); 
 281                     if (begin
.line 
!= lines
.size() || begin
.column 
- 1 != lines
.back().size() || error
->warning_
) { 
 282                         cy::position 
end(error
->location_
.end
); 
 284                         if (begin
.line 
!= lines
.size()) { 
 286                             std::cerr 
<< lines
[begin
.line 
- 1] << std::endl
; 
 290                         for (size_t i(0); i 
!= begin
.column 
- 1; ++i
) 
 292                         if (begin
.line 
!= end
.line 
|| begin
.column 
== end
.column
) 
 294                         else for (size_t i(0), e(end
.column 
- begin
.column
); i 
!= e
; ++i
) 
 296                         std::cerr 
<< std::endl
; 
 299                         std::cerr 
<< error
->message_ 
<< std::endl
; 
 301                         add_history(command
.c_str()); 
 307                 driver
.errors_
.clear(); 
 314             if (driver
.program_ 
== NULL
) 
 320                 std::ostringstream str
; 
 323                 out 
<< *driver
.program_
; 
 328         add_history(command
.c_str()); 
 332             std::cout 
<< code 
<< std::endl
; 
 334         Run(client
, code
, fout
, expand
); 
 337     if (append_history$ 
!= NULL
) { 
 338         _syscall(close(_syscall(open(histfile
, O_CREAT 
| O_WRONLY
, 0600)))); 
 339         (*append_history$
)(histlines
, histfile
); 
 341         write_history(histfile
); 
 348 static void *Map(const char *path
, size_t *psize
) { 
 350     _syscall(fd 
= open(path
, O_RDONLY
)); 
 353     _syscall(fstat(fd
, &stat
)); 
 354     size_t size(stat
.st_size
); 
 359     _syscall(base 
= mmap(NULL
, size
, PROT_READ
, MAP_SHARED
, fd
, 0)); 
 365 void InjectLibrary(pid_t pid
); 
 367 int Main(int argc
, char const * const argv
[], char const * const envp
[]) { 
 368     bool tty(isatty(STDIN_FILENO
)); 
 371     append_history$ 
= reinterpret_cast<int (*)(int, const char *)>(dlsym(RTLD_DEFAULT
, "append_history")); 
 374     pid_t 
pid(_not(pid_t
)); 
 379     _aprcall(apr_getopt_init(&state
, pool
, argc
, argv
)); 
 385         apr_status_t 
status(apr_getopt(state
, 
 399                     "usage: cycript [-c]" 
 403                     " [<script> [<arg>...]]\n" 
 418                 else if (strcmp(arg
, "bison") == 0) 
 422                     fprintf(stderr
, "invalid name for -g\n"); 
 429                 else if (strcmp(arg
, "minify") == 0) 
 432                     fprintf(stderr
, "invalid name for -n\n"); 
 439                 size_t size(strlen(arg
)); 
 442                 pid 
= strtoul(arg
, &end
, 0); 
 443                 if (arg 
+ size 
!= end
) { 
 444                     // XXX: arg needs to be escaped in some horrendous way of doom 
 445                     const char *command(apr_psprintf(pool
, "ps axc|sed -e '/^ *[0-9]/{s/^ *\\([0-9]*\\)\\( *[^ ]*\\)\\{3\\} *-*\\([^ ]*\\)/\\3 \\1/;/^%s /{s/^[^ ]* //;q;};};d'", arg
)); 
 447                     if (FILE *pids 
= popen(command
, "r")) { 
 452                             size_t read(fread(value 
+ size
, 1, sizeof(value
) - size
, pids
)); 
 457                                 if (size 
== sizeof(value
)) { 
 467                         if (value
[size 
- 1] == '\n') { 
 473                         size 
= strlen(value
); 
 474                         pid 
= strtoul(value
, &end
, 0); 
 475                         if (value 
+ size 
!= end
) fail
: 
 477                         _syscall(pclose(pids
)); 
 480                     if (pid 
== _not(pid_t
)) { 
 481                         fprintf(stderr
, "invalid pid for -p\n"); 
 498     if (pid 
!= _not(pid_t
) && ind 
< argc 
- 1) { 
 499         fprintf(stderr
, "-p cannot set argv\n"); 
 503     if (pid 
!= _not(pid_t
) && compile
) { 
 504         fprintf(stderr
, "-p conflicts with -c\n"); 
 513         // XXX: const_cast?! wtf gcc :( 
 514         CYSetArgs(argc 
- ind 
- 1, const_cast<const char **>(argv 
+ ind 
+ 1)); 
 517         if (strcmp(script
, "-") == 0) 
 522     if (pid 
!= _not(pid_t
) && script 
== NULL 
&& !tty
) { 
 523         fprintf(stderr
, "non-terminal attaching to remote console\n"); 
 531     if (pid 
== _not(pid_t
)) 
 534         int server(_syscall(socket(PF_UNIX
, SOCK_STREAM
, 0))); try { 
 535             struct sockaddr_un address
; 
 536             memset(&address
, 0, sizeof(address
)); 
 537             address
.sun_family 
= AF_UNIX
; 
 539             sprintf(address
.sun_path
, "/tmp/.s.cy.%u", getpid()); 
 541             _syscall(bind(server
, reinterpret_cast<sockaddr 
*>(&address
), SUN_LEN(&address
))); 
 542             _syscall(chmod(address
.sun_path
, 0777)); 
 545                 _syscall(listen(server
, 1)); 
 547                 client 
= _syscall(accept(server
, NULL
, NULL
)); 
 550                 unlink(address
.sun_path
); 
 554             _syscall(close(server
)); 
 562     if (script 
== NULL 
&& tty
) 
 563         Console(pool
, client
); 
 565         CYDriver 
driver(script 
?: "<stdin>"); 
 566         cy::parser 
parser(driver
); 
 567         Setup(driver
, parser
); 
 571         if (script 
== NULL
) { 
 575             driver
.file_ 
= stdin
; 
 578             start 
= reinterpret_cast<char *>(Map(script
, &size
)); 
 581             if (size 
>= 2 && start
[0] == '#' && start
[1] == '!') { 
 584                 if (void *line 
= memchr(start
, '\n', end 
- start
)) 
 585                     start 
= reinterpret_cast<char *>(line
); 
 590             driver
.data_ 
= start
; 
 591             driver
.size_ 
= end 
- start
; 
 594         if (parser
.parse() != 0 || !driver
.errors_
.empty()) { 
 595             for (CYDriver::Errors::const_iterator 
i(driver
.errors_
.begin()); i 
!= driver
.errors_
.end(); ++i
) 
 596                 std::cerr 
<< i
->location_
.begin 
<< ": " << i
->message_ 
<< std::endl
; 
 597         } else if (driver
.program_ 
!= NULL
) 
 599                 Run(client
, start
, end 
- start
, stdout
); 
 601                 std::ostringstream str
; 
 604                 out 
<< *driver
.program_
; 
 605                 std::string 
code(str
.str()); 
 609                     Run(client
, code
, stdout
); 
 616 int main(int argc
, char const * const argv
[], char const * const envp
[]) { 
 617     apr_status_t 
status(apr_app_initialize(&argc
, &argv
, &envp
)); 
 619     if (status 
!= APR_SUCCESS
) { 
 620         fprintf(stderr
, "apr_app_initialize() != APR_SUCCESS\n"); 
 623         return Main(argc
, argv
, envp
); 
 624     } catch (const CYException 
&error
) { 
 626         fprintf(stderr
, "%s\n", error
.PoolCString(pool
));