/*---------------------------------------------------------------------------- ChucK Concurrent, On-the-fly Audio Programming Language Compiler and Virtual Machine Copyright (c) 2004 Ge Wang and Perry R. Cook. All rights reserved. http://chuck.stanford.edu/ http://chuck.cs.princeton.edu/ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 U.S.A. -----------------------------------------------------------------------------*/ //----------------------------------------------------------------------------- // file: chuck_otf.cpp // desc: on-the-fly programming utilities // // author: Ge Wang (ge@ccrma.stanford.edu | gewang@cs.princeton.edu) // date: Autumn 2004 //----------------------------------------------------------------------------- #include "chuck_otf.h" #include "chuck_compile.h" #include "chuck_errmsg.h" #include "util_thread.h" #include "util_string.h" #include #include #include #include #include #include #ifndef __PLATFORM_WIN32__ #include #endif using namespace std; // extern "C" void signal_int( int ); // log level t_CKUINT g_otf_log = CK_LOG_INFO; //----------------------------------------------------------------------------- // name: otf_hton( ) // desc: ... //----------------------------------------------------------------------------- void otf_hton( Net_Msg * msg ) { msg->header = htonl( msg->header ); msg->type = htonl( msg->type ); msg->param = htonl( msg->param ); msg->param2 = htonl( msg->param2 ); msg->param3 = htonl( msg->param3 ); msg->length = htonl( msg->length ); } //----------------------------------------------------------------------------- // name: otf_ntoh( ) // desc: ... //----------------------------------------------------------------------------- void otf_ntoh( Net_Msg * msg ) { msg->header = ntohl( msg->header ); msg->type = ntohl( msg->type ); msg->param = ntohl( msg->param ); msg->param2 = ntohl( msg->param2 ); msg->param3 = ntohl( msg->param3 ); msg->length = ntohl( msg->length ); } //----------------------------------------------------------------------------- // name: recv_file() // desc: ... //----------------------------------------------------------------------------- FILE * recv_file( const Net_Msg & msg, ck_socket sock ) { Net_Msg buf; // what is left // t_CKUINT left = msg.param2; // make a temp file FILE * fd = tmpfile(); do { // msg if( !ck_recv( sock, (char *)&buf, sizeof(buf) ) ) goto error; otf_ntoh( &buf ); // write fwrite( buf.buffer, sizeof(char), buf.length, fd ); }while( buf.param2 > 0 ); // check for error if( buf.param2 == NET_ERROR ) { // this was a problem EM_log( CK_LOG_INFO, "(via otf): received error flag, dropping..." ); // done goto error; } return fd; error: fclose( fd ); fd = NULL; return NULL; } //----------------------------------------------------------------------------- // name: otf_process_msg() // desc: ... //----------------------------------------------------------------------------- t_CKUINT otf_process_msg( Chuck_VM * vm, Chuck_Compiler * compiler, Net_Msg * msg, t_CKBOOL immediate, void * data ) { Chuck_Msg * cmd = new Chuck_Msg; Chuck_VM_Code * code = NULL; FILE * fd = NULL; t_CKUINT ret = 0; // CK_FPRINTF_STDERR( "UDP message recv...\n" ); if( msg->type == MSG_REPLACE || msg->type == MSG_ADD ) { string filename; vector args; // parse out command line arguments if( !extract_args( msg->buffer, filename, args ) ) { // error CK_FPRINTF_STDERR( "[chuck]: malformed filename with argument list...\n" ); CK_FPRINTF_STDERR( " --> '%s'", msg->buffer ); SAFE_DELETE(cmd); goto cleanup; } // copy de-escaped filename strcpy( msg->buffer, filename.c_str() ); // copy args if needed if( args.size() > 0 ) { cmd->args = new vector; *(cmd->args) = args; } // see if entire file is on the way if( msg->param2 && msg->param2 != NET_ERROR ) { fd = recv_file( *msg, (ck_socket)data ); if( !fd ) { CK_FPRINTF_STDERR( "[chuck]: incoming source transfer '%s' failed...\n", mini(msg->buffer) ); SAFE_DELETE(cmd); goto cleanup; } } // construct full path to be associated with the file so me.sourceDir() works // (added 1.3.5.2) std::string full_path = get_full_path( msg->buffer ); // parse, type-check, and emit if( !compiler->go( msg->buffer, fd, NULL, full_path.c_str() ) ) { SAFE_DELETE(cmd); goto cleanup; } // get the code code = compiler->output(); // name it code->name += string(msg->buffer); // set the flags for the command cmd->type = msg->type; cmd->code = code; if( msg->type == MSG_REPLACE ) cmd->param = msg->param; } else if( msg->type == MSG_STATUS || msg->type == MSG_REMOVE || msg->type == MSG_REMOVEALL || msg->type == MSG_KILL || msg->type == MSG_TIME || msg->type == MSG_RESET_ID || msg->type == MSG_CLEARVM ) { cmd->type = msg->type; cmd->param = msg->param; } else if( msg->type == MSG_ABORT ) { // halt and clear current shred vm->abort_current_shred(); // short circuit ret = 1; SAFE_DELETE(cmd); goto cleanup; } else { CK_FPRINTF_STDERR( "[chuck]: unrecognized incoming command from network: '%li'\n", cmd->type ); SAFE_DELETE(cmd); goto cleanup; } // immediate if( immediate ) ret = vm->process_msg( cmd ); else { // queue message vm->queue_msg( cmd, 1 ); // return code ret = 1; } cleanup: // close file handle if( fd ) fclose( fd ); return ret; } //----------------------------------------------------------------------------- // name: otf_send_file() // desc: ... //----------------------------------------------------------------------------- int otf_send_file( const char * fname, Net_Msg & msg, const char * op, ck_socket dest ) { FILE * fd = NULL; struct stat fs; string filename; vector args; char buf[1024]; // parse out command line arguments if( !extract_args( fname, filename, args ) ) { // error CK_FPRINTF_STDERR( "[chuck]: malformed filename + argument list...\n" ); CK_FPRINTF_STDERR( " --> '%s'", fname ); return FALSE; } // filename and any args strcpy( msg.buffer, fname ); // test it strcpy( buf, filename.c_str() ); fd = open_cat_ck( buf ); if( !fd ) { CK_FPRINTF_STDERR( "[chuck]: cannot open file '%s' for [%s]...\n", filename.c_str(), op ); return FALSE; } if( !chuck_parse( (char *)filename.c_str(), fd ) ) { CK_FPRINTF_STDERR( "[chuck]: skipping file '%s' for [%s]...\n", filename.c_str(), op ); fclose( fd ); return FALSE; } // stat it memset( &fs, 0, sizeof(fs) ); stat( buf, &fs ); fseek( fd, 0, SEEK_SET ); // log EM_log( CK_LOG_INFO, "sending TCP file %s, size=%d", filename.c_str(), (t_CKUINT)fs.st_size ); EM_pushlog(); // send the first packet msg.param2 = (t_CKUINT)fs.st_size; msg.length = 0; otf_hton( &msg ); // go ck_send( dest, (char *)&msg, sizeof(msg) ); // send the whole thing t_CKUINT left = (t_CKUINT)fs.st_size; while( left ) { // log EM_log( CK_LOG_FINER, "%03d bytes left ... ", left ); // amount to send msg.length = left > NET_BUFFER_SIZE ? NET_BUFFER_SIZE : left; // read msg.param3 = fread( msg.buffer, sizeof(char), msg.length, fd ); // check for error if( !msg.param3 ) { // error encountered CK_FPRINTF_STDERR( "[chuck]: error while reading '%s'...\n", filename.c_str() ); // mark done left = 0; // error flag msg.param2 = NET_ERROR; } else { // amount left left -= msg.param3; // what's left msg.param2 = left; } // log EM_log( CK_LOG_FINER, "sending buffer %03d length %03d...", msg.param3, msg.length ); // send it otf_hton( &msg ); ck_send( dest, (char *)&msg, sizeof(msg) ); } // pop log EM_poplog(); // close fclose( fd ); return TRUE; } //----------------------------------------------------------------------------- // name: otf_send_connect() // desc: ... //----------------------------------------------------------------------------- ck_socket otf_send_connect( const char * host, int port ) { // log EM_log( CK_LOG_INFO, "otf connect: %s:%i", host, port ); ck_socket sock = ck_tcp_create( 0 ); if( !sock ) { CK_FPRINTF_STDERR( "[chuck]: cannot open socket to send command...\n" ); return NULL; } if( strcmp( host, "127.0.0.1" ) ) EM_log( g_otf_log, "connecting to %s on port %i via TCP...", host, port ); if( !ck_connect( sock, host, port ) ) { CK_FPRINTF_STDERR( "cannot open TCP socket on %s:%i...\n", host, port ); ck_close( sock ); return NULL; } ck_send_timeout( sock, 0, 2000000 ); return sock; } //----------------------------------------------------------------------------- // name: otf_send_cmd() // desc: ... //----------------------------------------------------------------------------- int otf_send_cmd( int argc, const char ** argv, t_CKINT & i, const char * host, int port, int * is_otf ) { Net_Msg msg; // REFACTOR-2017 TODO: wat is global sigpipe mode // g_sigpipe_mode = 1; int tasks_total = 0, tasks_done = 0; ck_socket dest = NULL; if( is_otf ) *is_otf = TRUE; // log EM_log( CK_LOG_INFO, "examining otf command '%s'...", argv[i] ); if( !strcmp( argv[i], "--add" ) || !strcmp( argv[i], "+" ) ) { if( ++i >= argc ) { CK_FPRINTF_STDERR( "[chuck]: not enough arguments following [add]...\n" ); goto error; } if( !(dest = otf_send_connect( host, port )) ) return 0; EM_pushlog(); do { // log EM_log( CK_LOG_INFO, "sending file:args '%s' for add...", mini(argv[i]) ); msg.type = MSG_ADD; msg.param = 1; tasks_done += otf_send_file( argv[i], msg, "add", dest ); tasks_total++; } while( ++i < argc ); // log EM_poplog(); if( !tasks_done ) goto error; } else if( !strcmp( argv[i], "--remove" ) || !strcmp( argv[i], "-" ) ) { if( ++i >= argc ) { CK_FPRINTF_STDERR( "[chuck]: not enough arguments following [remove]...\n" ); goto error; } if( !(dest = otf_send_connect( host, port )) ) return 0; EM_pushlog(); do { // log EM_log( CK_LOG_INFO, "requesting removal of shred '%i'..." ); msg.param = atoi( argv[i] ); msg.type = MSG_REMOVE; otf_hton( &msg ); ck_send( dest, (char *)&msg, sizeof(msg) ); } while( ++i < argc ); // log EM_poplog(); } else if( !strcmp( argv[i], "--" ) ) { if( !(dest = otf_send_connect( host, port )) ) return 0; EM_pushlog(); // log EM_log( CK_LOG_INFO, "requesting removal of last shred..." ); msg.param = 0xffffffff; msg.type = MSG_REMOVE; otf_hton( &msg ); ck_send( dest, (char *)&msg, sizeof(msg) ); // log EM_poplog(); } else if( !strcmp( argv[i], "--replace" ) || !strcmp( argv[i], "=" ) ) { if( ++i >= argc ) { CK_FPRINTF_STDERR( "[chuck]: not enough arguments following [replace]...\n" ); goto error; } if( i <= 0 ) msg.param = 0xffffffff; else msg.param = atoi( argv[i] ); if( ++i >= argc ) { CK_FPRINTF_STDERR( "[chuck]: not enough arguments following [replace]...\n" ); goto error; } if( !(dest = otf_send_connect( host, port )) ) return 0; EM_pushlog(); EM_log( CK_LOG_INFO, "requesting replace shred '%i' with '%s'...", msg.param, mini(argv[i]) ); msg.type = MSG_REPLACE; if( !otf_send_file( argv[i], msg, "replace", dest ) ) goto error; EM_poplog(); } else if( !strcmp( argv[i], "--removeall" ) || !strcmp( argv[i], "--remall" ) || !strcmp( argv[i], "--remove.all" ) ) { if( !(dest = otf_send_connect( host, port )) ) return 0; EM_pushlog(); EM_log( CK_LOG_INFO, "requesting removeall..." ); msg.type = MSG_REMOVEALL; msg.param = 0; otf_hton( &msg ); ck_send( dest, (char *)&msg, sizeof(msg) ); EM_poplog(); } else if( !strcmp( argv[i], "--clear.vm" ) ) { if( !(dest = otf_send_connect( host, port )) ) return 0; EM_pushlog(); EM_log( CK_LOG_INFO, "requesting clearvm..." ); msg.type = MSG_CLEARVM; msg.param = 0; otf_hton( &msg ); ck_send( dest, (char *)&msg, sizeof(msg) ); EM_poplog(); } else if( !strcmp( argv[i], "--kill" ) ) { if( !(dest = otf_send_connect( host, port )) ) return 0; msg.type = MSG_REMOVEALL; msg.param = 0; otf_hton( &msg ); ck_send( dest, (char *)&msg, sizeof(msg) ); msg.type = MSG_KILL; msg.param = (i+1)otf_socket ); // check for thread shutdown, potentially occured during ck_accept if(!carrier->otf_thread) break; if( !client ) { if( carrier->vm ) EM_log( CK_LOG_INFO, "[chuck]: socket error during accept()...\n" ); usleep( 40000 ); // ck_close( client ); don't close NULL client continue; } msg.clear(); // set time out ck_recv_timeout( client, 0, 5000000 ); n = ck_recv( client, (char *)&msg, sizeof(msg) ); otf_ntoh( &msg ); if( n != sizeof(msg) ) { CK_FPRINTF_STDERR( "[chuck]: 0-length packet...\n" ); usleep( 40000 ); ck_close( client ); continue; } if( msg.header != NET_HEADER ) { CK_FPRINTF_STDERR( "[chuck]: header mismatch - possible endian lunacy...\n" ); ck_close( client ); continue; } while( msg.type != MSG_DONE ) { if( carrier->vm ) { if( !otf_process_msg( carrier->vm, carrier->compiler, &msg, FALSE, client ) ) { ret.param = FALSE; strcpy( (char *)ret.buffer, EM_lasterror() ); while( msg.type != MSG_DONE && n ) { n = ck_recv( client, (char *)&msg, sizeof(msg) ); otf_ntoh( &msg ); } break; } else { ret.param = TRUE; strcpy( (char *)ret.buffer, "success" ); n = ck_recv( client, (char *)&msg, sizeof(msg) ); otf_ntoh( &msg ); } } else { usleep( 10000 ); } } otf_hton( &ret ); ck_send( client, (char *)&ret, sizeof(ret) ); ck_close( client ); } return NULL; } const char * poop[] = { "[chuck]: bus error...", "[chuck]: segmentation fault, core dumped...", "[chuck]: access violation, NULL pointer...", "[chuck]: Unhandled exception in chuck: 0xC0000005: Access violation", "[chuck]: malloc: damage after normal block...", "[chuck]: virtual machine panic!!!", "[chuck]: confused by earlier errors, bailing out...", "[chuck]: lack of destructors have led to the unrecoverable mass build-up of trash\n" " the chuck garbage collector will now run, deleting all files (bye.)", "[chuck]: calling machine.crash()...", "[chuck]: an unknown fatal error has occurred. please restart your computer...", "[chuck]: an unknown fatal error has occurred. please reinstall your OS...", "[chuck]: an unknown fatal error has occurred. please update to chuck-3.0", "[chuck]: internal error: unknown error", "[chuck]: page fault!!!", "[chuck]: error printing error message. cannot continue 2#%%HGAFf9a0x" }; long poop_size = sizeof( poop ) / sizeof( char * ); //----------------------------------------------------------------------------- // name: uh() // desc: ... //----------------------------------------------------------------------------- void uh( ) { srand( time( NULL ) ); while( true ) { int n = (int)(rand() / (float)RAND_MAX * poop_size); printf( "%s\n", poop[n] ); usleep( (unsigned long)(rand() / (float)RAND_MAX * 2000000) / 10 ); } }