/** * Load a program by reading it from the terminal program over the serial line. * LOAD "<filename>" */ void cmd_load(char *args) { char *filename; if (parse_string_expression(args, &filename)) { cmd_new(0); lcd_puts("Loading..."); acia_puts("*LOAD \""); acia_puts(filename); acia_puts("\"\n"); for(;;) { acia_puts("*NEXT\n"); acia_gets(readline_buffer, 255); if (strncmp("*EOF", readline_buffer, 4) == 0) { break; } else if (strncmp("!NOTFOUND", readline_buffer, 9) == 0) { lcd_put_newline(); syntax_error_msg("File not found"); break; } else { lcd_putc('.'); interpret(readline_buffer); } } if (! error) { lcd_put_newline(); print_ready(); } } else { syntax_error_invalid_argument(); } }
void serial_process(void) { uint8_t serial_channel = serial_readFrame(); packet_t *packet; if( serial_channel == 0 ){ return; } if( serial_channel == 0xFF || serial_channel == 0xFE ){ display_data(serial_channel, serial_getMessage(), serial_getMessageLen()); return; } if( serial_getMessageLen() == 16 ){ uint8_t *msg = serial_getMessage(); if( serial_channel == '0' ){ packet = (packet_t *) msg; if( packet_check_magic(packet) ){ cmd_new(packet->cmd, packet->data); } }else if(serial_channel != 0) { bus_sendFrame(serial_channel, msg, 16); } } }
void cmd_result(char *s) { #if 0 char results[10], comment[80]; sscanf(s, " %s %s", results, comment); printf("read result '%s', comment '%s'\n", results, comment); if (book_mode) { #ifdef NOT if (!strcmp(results, "1/2-1/2")) { hardupdatebook(-1, bookfile); } else if (!strcmp(results,"1-0")) { hardupdatebook(WHITE, bookfile); } else if (!strcmp(results, "0-1")) { hardupdatebook(BLACK, bookfile); } #endif } cmd_new(NULL); #endif }
/* start parsing the input line */ static void parse_input(cmds* cmd) { cmds* elem; /* examine first token */ scan(); if (lookahead.kind==END) { return; } /* create new command element, add it to the list, and parse it */ elem = cmd_new(); if (cmd==NULL) { root=cmd=elem; } else { cmd->next = elem; } parse_pipe(elem, &elem->prog); /* parse next command from input */ parse_input(elem); }
static int process_command(int serial, int cid, char *cmd) { /*char *opt, *opt2; */ char *p, *opt; int ret; if ((p = strchr(cmd, '\n'))) *p = '\0'; else return -1; if ((opt = strchr(cmd, ' '))) { *opt = '\0'; opt ++; } else { opt = NULL; } debug_printf(DEBUG_NOTE, "cmd: %s\n", cmd); if (strcmp(cmd, "RELEASE") == 0) ret = cmd_release(cid); else if (strcmp(cmd, "UNFOCUSED") == 0) ret = cmd_unfocused(cid); else if (strcmp(cmd, "FOCUSED") == 0) ret = cmd_focused(cid); else if (strcmp(cmd, "HIDE") == 0) ret = cmd_hide(cid); else if (strcmp(cmd, "SHOW") == 0) ret = cmd_show(cid); else if (strcmp(cmd, "NEW") == 0) ret = cmd_new(cid, opt); else if (strcmp(cmd, "RESET") == 0) ret = cmd_reset(cid); else if (strcmp(cmd, "CHANGE") == 0) ret = cmd_change(cid, opt); else if (strcmp(cmd, "PROP") == 0) ret = cmd_prop(cid, opt); else if (strcmp(cmd, "LABEL") == 0) ret = cmd_label(cid); else if (strcmp(cmd, "HELPER") == 0) ret = cmd_helper(cid, opt); else if (strcmp(cmd, "NOP") == 0) ret = cmd_nop(cid); else if (strcmp(cmd, "LIST") == 0) ret = cmd_list(); else if (strcmp(cmd, "SETENC") == 0) ret = cmd_setenc(opt); else if (strcmp(cmd, "GETENC") == 0) ret = cmd_getenc(opt); /* for debug */ else ret = cmd_error(); return ret; }
void serial_process(void) { uint8_t serial_channel = serial_readFrame(); packet_t *packet; if( serial_getMessageLen() == 16 ){ uint8_t *msg = serial_getMessage(); if( serial_channel == '0' ){ aes_decrypt(msg); packet = (packet_t *) msg; if( packet_check(packet) ){ cmd_new(packet->cmd, packet->data); } }else if(serial_channel != 0) { bus_sendFrame(serial_channel, msg, 16); } } }
void cmd_end(char *s) { end(-2, "game ended"); output("ending according to user\n"); cmd_new(NULL); }
static CMD * make1cmds( TARGET *t ) { CMD *cmds = 0; LIST *shell = 0; module_t *settings_module = 0; TARGET *settings_target = 0; /* Step through actions */ /* Actions may be shared with other targets or grouped with */ /* RULE_TOGETHER, so actions already seen are skipped. */ ACTIONS* a0; for(a0 = t->actions ; a0; a0 = a0->next ) { RULE *rule = a0->action->rule; rule_actions *actions = rule->actions; SETTINGS *boundvars; LIST *nt, *ns; ACTIONS *a1; int start, chunk, length; /* Only do rules with commands to execute. */ /* If this action has already been executed, use saved status */ if( !actions || a0->action->running ) continue; a0->action->running = 1; /* Make LISTS of targets and sources */ /* If `execute together` has been specified for this rule, tack */ /* on sources from each instance of this rule for this target. */ nt = make1list( L0, a0->action->targets, 0 ); ns = make1list( L0, a0->action->sources, actions->flags ); if( actions->flags & RULE_TOGETHER ) for( a1 = a0->next; a1; a1 = a1->next ) if( a1->action->rule == rule && !a1->action->running ) { ns = make1list( ns, a1->action->sources, actions->flags ); a1->action->running = 1; } /* If doing only updated (or existing) sources, but none have */ /* been updated (or exist), skip this action. */ if( !ns && ( actions->flags & ( RULE_NEWSRCS | RULE_EXISTING ) ) ) { list_free( nt ); continue; } swap_settings( &settings_module, &settings_target, rule->module, t ); if (!shell) shell = var_get( "JAMSHELL" ); /* shell is per-target */ /* If we had 'actions xxx bind vars' we bind the vars now */ boundvars = make1settings( actions->bindlist ); pushsettings( boundvars ); /* * Build command, starting with all source args. * * If cmd_new returns 0, it's because the resulting command * length is > MAXLINE. In this case, we'll slowly reduce * the number of source arguments presented until it does * fit. This only applies to actions that allow PIECEMEAL * commands. * * While reducing slowly takes a bit of compute time to get * things just right, it's worth it to get as close to MAXLINE * as possible, because launching the commands we're executing * is likely to be much more compute intensive! * * Note we loop through at least once, for sourceless actions. */ start = 0; chunk = length = list_length( ns ); do { /* Build cmd: cmd_new consumes its lists. */ CMD *cmd = cmd_new( rule, list_copy( L0, nt ), list_sublist( ns, start, chunk ), list_copy( L0, shell ) ); if( cmd ) { /* It fit: chain it up. */ if( !cmds ) cmds = cmd; else cmds->tail->next = cmd; cmds->tail = cmd; start += chunk; } else if( ( actions->flags & RULE_PIECEMEAL ) && chunk > 1 ) { /* Reduce chunk size slowly. */ chunk = chunk * 9 / 10; } else { /* Too long and not splittable. */ printf( "%s actions too long (max %d):\n", rule->name, MAXLINE ); /* Tell the user what didn't fit */ cmd = cmd_new( rule, list_copy( L0, nt ), list_sublist( ns, start, chunk ), list_new( L0, newstr( "%" ) ) ); printf( cmd->buf ); exit( EXITBAD ); } } while( start < length ); /* These were always copied when used. */ list_free( nt ); list_free( ns ); /* Free the variables whose values were bound by */ /* 'actions xxx bind vars' */ popsettings( boundvars ); freesettings( boundvars ); } swap_settings( &settings_module, &settings_target, 0, 0 ); return cmds; }
static CMD * make1cmds( TARGET * t ) { CMD * cmds = 0; CMD * * cmds_next = &cmds; LIST * shell = L0; module_t * settings_module = 0; TARGET * settings_target = 0; ACTIONS * a0; int const running_flag = globs.noexec ? A_RUNNING_NOEXEC : A_RUNNING; /* Step through actions. Actions may be shared with other targets or grouped * using RULE_TOGETHER, so actions already seen are skipped. */ for ( a0 = t->actions; a0; a0 = a0->next ) { RULE * rule = a0->action->rule; rule_actions * actions = rule->actions; SETTINGS * boundvars; LIST * nt; LIST * ns; ACTIONS * a1; /* Only do rules with commands to execute. If this action has already * been executed, use saved status. */ if ( !actions || a0->action->running >= running_flag ) continue; a0->action->running = running_flag; /* Make LISTS of targets and sources. If `execute together` has been * specified for this rule, tack on sources from each instance of this * rule for this target. */ nt = make1list( L0, a0->action->targets, 0 ); ns = make1list( L0, a0->action->sources, actions->flags ); if ( actions->flags & RULE_TOGETHER ) for ( a1 = a0->next; a1; a1 = a1->next ) if ( a1->action->rule == rule && a1->action->running < running_flag ) { ns = make1list( ns, a1->action->sources, actions->flags ); a1->action->running = running_flag; } /* If doing only updated (or existing) sources, but none have been * updated (or exist), skip this action. */ if ( list_empty( ns ) && ( actions->flags & ( RULE_NEWSRCS | RULE_EXISTING ) ) ) { list_free( nt ); continue; } swap_settings( &settings_module, &settings_target, rule->module, t ); if ( list_empty( shell ) ) { /* shell is per-target */ shell = var_get( rule->module, constant_JAMSHELL ); } /* If we had 'actions xxx bind vars' we bind the vars now. */ boundvars = make1settings( rule->module, actions->bindlist ); pushsettings( rule->module, boundvars ); /* * Build command, starting with all source args. * * For actions that allow PIECEMEAL commands, if the constructed command * string is too long, we retry constructing it with a reduced number of * source arguments presented. * * While reducing slowly takes a bit of compute time to get things just * right, it is worth it to get as close to maximum allowed command * string length as possible, because launching the commands we are * executing is likely to be much more compute intensive. * * Note that we loop through at least once, for sourceless actions. */ { int const length = list_length( ns ); int start = 0; int chunk = length; LIST * cmd_targets = L0; LIST * cmd_shell = L0; do { CMD * cmd; int cmd_check_result; int cmd_error_length; int cmd_error_max_length; int retry = 0; int accept_command = 0; /* Build cmd: cmd_new() takes ownership of its lists. */ if ( list_empty( cmd_targets ) ) cmd_targets = list_copy( nt ); if ( list_empty( cmd_shell ) ) cmd_shell = list_copy( shell ); cmd = cmd_new( rule, cmd_targets, list_sublist( ns, start, chunk ), cmd_shell ); cmd_check_result = exec_check( cmd->buf, &cmd->shell, &cmd_error_length, &cmd_error_max_length ); if ( cmd_check_result == EXEC_CHECK_OK ) { accept_command = 1; } else if ( cmd_check_result == EXEC_CHECK_NOOP ) { accept_command = 1; cmd->noop = 1; } else if ( ( actions->flags & RULE_PIECEMEAL ) && ( chunk > 1 ) ) { /* Too long but splittable. Reduce chunk size slowly and * retry. */ assert( cmd_check_result == EXEC_CHECK_TOO_LONG || cmd_check_result == EXEC_CHECK_LINE_TOO_LONG ); chunk = chunk * 9 / 10; retry = 1; } else { /* Too long and not splittable. */ char const * const error_message = cmd_check_result == EXEC_CHECK_TOO_LONG ? "is too long" : "contains a line that is too long"; assert( cmd_check_result == EXEC_CHECK_TOO_LONG || cmd_check_result == EXEC_CHECK_LINE_TOO_LONG ); printf( "%s action %s (%d, max %d):\n", object_str( rule->name ), error_message, cmd_error_length, cmd_error_max_length ); /* Tell the user what did not fit. */ fputs( cmd->buf->value, stdout ); exit( EXITBAD ); } assert( !retry || !accept_command ); if ( accept_command ) { /* Chain it up. */ *cmds_next = cmd; cmds_next = &cmd->next; /* Mark lists we need recreated for the next command since * they got consumed by the cmd object. */ cmd_targets = L0; cmd_shell = L0; } else { /* We can reuse targets & shell lists for the next command * if we do not let them die with this cmd object. */ cmd_release_targets_and_shell( cmd ); cmd_free( cmd ); } if ( !retry ) start += chunk; } while ( start < length ); } /* These were always copied when used. */ list_free( nt ); list_free( ns ); /* Free variables with values bound by 'actions xxx bind vars'. */ popsettings( rule->module, boundvars ); freesettings( boundvars ); } swap_settings( &settings_module, &settings_target, 0, 0 ); return cmds; }
cmd_response_t cmd_run(struct worker *w, struct http_client *client, const char *uri, size_t uri_len, const char *body, size_t body_len) { char *qmark = memchr(uri, '?', uri_len); char *slash; const char *p, *cmd_name = uri; int cmd_len; int param_count = 0, cur_param = 1; struct cmd *cmd; formatting_fun f_format; /* count arguments */ if(qmark) { uri_len = qmark - uri; } for(p = uri; p && p < uri + uri_len; param_count++) { p = memchr(p+1, '/', uri_len - (p+1-uri)); } if(body && body_len) { /* PUT request */ param_count++; } if(param_count == 0) { return CMD_PARAM_ERROR; } cmd = cmd_new(param_count); cmd->fd = client->fd; cmd->database = w->s->cfg->database; /* get output formatting function */ uri_len = cmd_select_format(client, cmd, uri, uri_len, &f_format); /* add HTTP info */ cmd_setup(cmd, client); /* check if we only have one command or more. */ slash = memchr(uri, '/', uri_len); if(slash) { /* detect DB number by checking if first arg is only numbers */ int has_db = 1; int db_num = 0; for(p = uri; p < slash; ++p) { if(*p < '0' || *p > '9') { has_db = 0; break; } db_num = db_num * 10 + (*p - '0'); } /* shift to next arg if a db was set up */ if(has_db) { char *next; cmd->database = db_num; cmd->count--; /* overcounted earlier */ cmd_name = slash + 1; if((next = memchr(cmd_name, '/', uri_len - (slash - uri)))) { cmd_len = next - cmd_name; } else { cmd_len = uri_len - (slash - uri + 1); } } else { cmd_len = slash - uri; } } else { cmd_len = uri_len; } /* there is always a first parameter, it's the command name */ cmd->argv[0] = malloc(cmd_len); memcpy(cmd->argv[0], cmd_name, cmd_len); cmd->argv_len[0] = cmd_len; /* check that the client is able to run this command */ if(!acl_allow_command(cmd, w->s->cfg, client)) { cmd_free(cmd); return CMD_ACL_FAIL; } if(cmd_is_subscribe(cmd)) { /* create a new connection to Redis */ cmd->ac = (redisAsyncContext*)pool_connect(w->pool, cmd->database, 0); /* register with the client, used upon disconnection */ client->pub_sub = cmd; cmd->pub_sub_client = client; } else if(cmd->database != w->s->cfg->database) { /* create a new connection to Redis for custom DBs */ cmd->ac = (redisAsyncContext*)pool_connect(w->pool, cmd->database, 0); } else { /* get a connection from the pool */ cmd->ac = (redisAsyncContext*)pool_get_context(w->pool); } /* no args (e.g. INFO command) */ if(!slash) { if(!cmd->ac) { cmd_free(cmd); return CMD_REDIS_UNAVAIL; } redisAsyncCommandArgv(cmd->ac, f_format, cmd, 1, (const char **)cmd->argv, cmd->argv_len); return CMD_SENT; } p = cmd_name + cmd_len + 1; while(p < uri + uri_len) { const char *arg = p; int arg_len; char *next = memchr(arg, '/', uri_len - (arg-uri)); if(!next || next > uri + uri_len) { /* last argument */ p = uri + uri_len; arg_len = p - arg; } else { /* found a slash */ arg_len = next - arg; p = next + 1; } /* record argument */ cmd->argv[cur_param] = decode_uri(arg, arg_len, &cmd->argv_len[cur_param], 1); cur_param++; } if(body && body_len) { /* PUT request */ cmd->argv[cur_param] = malloc(body_len); memcpy(cmd->argv[cur_param], body, body_len); cmd->argv_len[cur_param] = body_len; } /* send it off! */ if(cmd->ac) { cmd_send(cmd, f_format); return CMD_SENT; } /* failed to find a suitable connection to Redis. */ cmd_free(cmd); client->pub_sub = NULL; return CMD_REDIS_UNAVAIL; }
static CMD * make1cmds( ACTIONS *a0 ) #endif { CMD *cmds = 0; LIST *shell = var_get( "JAMSHELL" ); /* shell is per-target */ /* Step through actions */ /* Actions may be shared with other targets or grouped with */ /* RULE_TOGETHER, so actions already seen are skipped. */ for( ; a0; a0 = a0->next ) { RULE *rule = a0->action->rule; SETTINGS *boundvars; LIST *nt, *ns; ACTIONS *a1; int start, chunk, length, maxline; TARGETS *autosettingsreverse = 0; TARGETS *autot; #ifdef OPT_MULTIPASS_EXT if ( a0->action->pass != actionpass ) continue; #endif /* Only do rules with commands to execute. */ /* If this action has already been executed, use saved status */ #ifdef OPT_DEBUG_MAKE1_LOG_EXT if (DEBUG_MAKE1) { printf( "make1cmds\t--\t%s (actions %s, running %s)\n" , rule->name, rule->actions ? "yes" : "no", a0->action->running ? "yes" : "no" ); } #endif if( !rule->actions || a0->action->running ) continue; #ifdef OPT_REMOVE_EMPTY_DIRS_EXT if ( rule->flags & RULE_REMOVEEMPTYDIRS ) { for( a1 = a0; a1; a1 = a1->next ) { TARGETS* sources; for ( sources = a1->action->sources; sources; sources = sources->next ) { emptydirtargets = list_new( emptydirtargets, sources->target->name, 1 ); } } } #endif for ( autot = a0->action->autosettings; autot; autot = autot->next ) { if ( autot->target != t ) pushsettings( autot->target->settings ); autosettingsreverse = targetentryhead( autosettingsreverse, autot->target, 0 ); } pushsettings( t->settings ); a0->action->running = 1; #ifdef OPT_ACTIONS_WAIT_FIX a0->action->run_tgt = t; #endif /* Make LISTS of targets and sources */ /* If `execute together` has been specified for this rule, tack */ /* on sources from each instance of this rule for this target. */ #ifdef OPT_DEBUG_MAKE1_LOG_EXT if (DEBUG_MAKE1) { LIST *list = make1list(L0, a0->action->targets, 0); printf("make1cmds\t--\ttargets: "); list_print(list); list_free(list); printf("\n"); list = make1list(L0, a0->action->sources, 0); printf("make1cmds\t--\tsources: "); list_print(list); list_free(list); printf("\n"); } #endif #ifdef OPT_BUILTIN_MD5CACHE_EXT if (t->filecache_generate || t->filecache_use) { LIST* targets = make1list_unbound( L0, a0->action->targets, 0 ); LIST* sources = make1list_unbound( L0, a0->action->sources, rule->flags ); nt = L0; ns = L0; if ( strncmp( rule->name, "batched_", 8 ) == 0 ) { int anycacheable = 0; for( ; targets; targets = targets->next, sources = ( sources == NULL ? sources : sources->next ) ) { TARGET *t = bindtarget(targets->string); TARGET *s = sources!=NULL ? bindtarget(sources->string) : NULL; /* if this target could be cacheable */ if ( (t->flags & T_FLAG_USEFILECACHE) && (t->filecache_generate || t->filecache_use) ) { /* find its final md5sum */ MD5_CTX context; MD5SUM buildsumorg; anycacheable = 1; memcpy(&buildsumorg, &t->buildmd5sum, sizeof(t->buildmd5sum)); MD5Init( &context ); MD5Update( &context, t->buildmd5sum, sizeof( t->buildmd5sum ) ); { TARGET *outt = bindtarget( t->boundname ); outt->flags |= T_FLAG_USEFILECACHE; MD5Final( outt->buildmd5sum, &context ); memcpy(&t->buildmd5sum, &outt->buildmd5sum, sizeof(t->buildmd5sum)); } if (DEBUG_MD5HASH) { printf( "Cacheable: %s buildmd5: %s org: %s\n", t->boundname, md5tostring(t->buildmd5sum), md5tostring(buildsumorg) ); } /* if using cache is allowed */ if (t->filecache_use) { const char *cachedname; /* if the target is available in the cache */ cachedname = filecache_getfilename(t, t->buildmd5sum, ".doesntwork"); if (cachedname!=NULL) { time_t cachedtime; if ( file_time( cachedname, &cachedtime ) == 0 ) { /* try to get it from the cache */ if (copyfile(t->boundname, cachedname, NULL)) { printf( "Using cached %s\n", t->name ); continue; } else { printf( "Cannot retrieve %s from cache (will build normally)\n", t->name ); } } else { if( DEBUG_MD5HASH) { printf( "Cannot find %s in cache as %s\n", t->name, cachedname ); } } } } /* Build new lists */ nt = list_new( nt, t->boundname, 1 ); if (s) ns = list_new( ns, s->boundname, 1 ); } } if ( !anycacheable ) { nt = make1list( L0, a0->action->targets, 0 ); ns = make1list( L0, a0->action->sources, rule->flags ); } } else { int allcached = 1; popsettings( t->settings ); for( ; targets; targets = list_next(targets) ) { TARGET *t = bindtarget(targets->string); // TARGET *s = sources!=NULL ? bindtarget(sources->string) : NULL; TARGETS *c; TARGET *outt; LIST *filecache = 0; if ( t->flags & T_FLAG_USEFILECACHE ) { pushsettings( t->settings ); filecache = filecache_fillvalues( t ); popsettings( t->settings ); } /* if this target could be cacheable */ if ( (t->flags & T_FLAG_USEFILECACHE) && (t->filecache_generate || t->filecache_use) ) { /* find its final md5sum */ MD5_CTX context; if( DEBUG_MD5HASH ) { printf( "------------------------------------------------\n" ); printf( "------------------------------------------------\n" ); printf( "------------------------------------------------\n" ); } /* sort all dependents by name, so we can make reliable md5sums */ t->depends = make0sortbyname( t->depends ); MD5Init( &context ); /* add the path of the file to the sum - it is significant because one command can create more than one file */ MD5Update( &context, (unsigned char*)t->name, (unsigned int)strlen( t->name ) ); /* add in the COMMANDLINE */ if ( t->flags & T_FLAG_USECOMMANDLINE ) { SETTINGS *vars; for ( vars = t->settings; vars; vars = vars->next ) { if ( vars->symbol[0] == 'C' && strcmp( vars->symbol, "COMMANDLINE" ) == 0 ) { LIST *list; for ( list = vars->value; list; list = list->next ) { MD5Update( &context, (unsigned char*)list->string, (unsigned int)strlen( list->string ) ); if( DEBUG_MD5HASH ) printf( "\t\tCOMMANDLINE: %s\n", list->string ); } break; } } } /* for each dependencies */ for( c = t->depends; c; c = c->next ) { /* If this is a "Needs" dependency, don't care about its contents. */ if (c->needs) { continue; } /* add name of the dependency and its contents */ make0calcmd5sum( c->target, 1 ); if ( c->target->buildmd5sum_calculated ) { MD5Update( &context, (unsigned char*)c->target->name, (unsigned int)strlen( c->target->name ) ); MD5Update( &context, c->target->buildmd5sum, sizeof( c->target->buildmd5sum ) ); } } outt = bindtarget( t->boundname ); outt->flags |= T_FLAG_USEFILECACHE; outt->filecache_generate = t->filecache_generate; outt->filecache_use = t->filecache_use; outt->settings = addsettings( outt->settings, VAR_SET, "FILECACHE", list_new( L0, filecache->string, 1 ) ); MD5Final( outt->buildmd5sum, &context ); if (DEBUG_MD5HASH) { printf( "Cacheable: %s buildmd5: %s\n", t->boundname, md5tostring(outt->buildmd5sum) ); } /* if using cache is allowed */ if ( t->filecache_use && allcached ) { allcached = filecache_retrieve( t, outt->buildmd5sum ); } else { allcached = 0; } } else { allcached = 0; } } pushsettings( t->settings ); if ( !allcached ) { nt = make1list( L0, a0->action->targets, 0 ); ns = make1list( L0, a0->action->sources, rule->flags ); } } list_free( targets ); list_free( sources ); /* if no targets survived (all were retrieved from the cache) or no sources survived (all are up to date) */ if (nt==NULL) { // || ns==NULL) { /* skip this action */ list_free(ns); popsettings( t->settings ); for ( autot = autosettingsreverse; autot; autot = autot->next ) { if ( autot->target != t ) pushsettings( autot->target->settings ); } continue; } } else { #if 0 if ( strncmp( rule->name, "batched_", 8 ) == 0 ) { TARGETS* targets = a0->action->targets; TARGETS* sources = a0->action->sources; int anycacheable = 0; nt = L0; ns = L0; /* walk sources and targets simultaneously */ for( ; targets; targets = targets->next, sources = (sources==NULL?sources:sources->next) ) { TARGET *t = targets->target; TARGET *s = sources!=NULL ? sources->target : NULL; /* Sources to 'actions existing' are never in the dependency */ /* graph (if they were, they'd get built and 'existing' would */ /* be superfluous, so throttle warning message about independent */ /* targets. */ if( t->binding == T_BIND_UNBOUND ) make1bind( t, 0 ); if( s!=NULL) { if ( s->binding == T_BIND_UNBOUND ) make1bind( s, !( rule->flags & RULE_EXISTING ) ); if ( s->binding == T_BIND_UNBOUND ) printf("Warning using unbound source %s for batched action.\n", s->name); } if( ( rule->flags & RULE_EXISTING ) && s!=NULL && s->binding != T_BIND_EXISTS ) continue; if( t->fate < T_FATE_BUILD ) continue; /* Build new lists */ nt = list_new( nt, t->boundname, 1 ); if (s!=NULL) { ns = list_new( ns, s->boundname, 1 ); } } if (sources!=NULL) { printf("warning: more sources than targets in a batched action!\n"); } } else { #endif nt = make1list( L0, a0->action->targets, 0 ); ns = make1list( L0, a0->action->sources, rule->flags ); #if 0 } #endif } #else nt = make1list( L0, a0->action->targets, 0 ); ns = make1list( L0, a0->action->sources, rule->flags ); #endif if( rule->flags & RULE_TOGETHER ) for( a1 = a0->next; a1; a1 = a1->next ) #ifdef OPT_MULTIPASS_EXT if( a1->action->pass == actionpass && a1->action->rule == rule && !a1->action->running ) #else if( a1->action->rule == rule && !a1->action->running ) #endif { ns = make1list( ns, a1->action->sources, rule->flags ); a1->action->running = 1; #ifdef OPT_ACTIONS_WAIT_FIX a1->action->run_tgt = t; #endif } /* If doing only updated (or existing) sources, but none have */ /* been updated (or exist), skip this action. */ if( !ns && ( rule->flags & ( RULE_UPDATED | RULE_EXISTING ) ) ) { list_free( nt ); #ifdef OPT_DEBUG_MAKE1_LOG_EXT if (DEBUG_MAKE1) { const char* desc = 0; if ((rule->flags & (RULE_UPDATED | RULE_EXISTING)) == (RULE_UPDATED | RULE_EXISTING)) { desc = "updated/existing"; } else if (rule->flags & RULE_UPDATED) { desc = "updated"; } else if (rule->flags & RULE_EXISTING) { desc = "existing"; } printf( "make1cmds\t--\t%s (skipping actions by %s)\n" , rule->name, desc ); } #endif /* OPT_DEBUG_MAKE1_LOG_EXT */ popsettings( t->settings ); for ( autot = autosettingsreverse; autot; autot = autot->next ) { if ( autot->target != t ) pushsettings( autot->target->settings ); } continue; } /* If we had 'actions xxx bind vars' we bind the vars now */ boundvars = make1settings( rule->bindlist ); pushsettings( boundvars ); /* * Build command, starting with all source args. * * If cmd_new returns 0, it's because the resulting command * length is > MAXLINE. In this case, we'll slowly reduce * the number of source arguments presented until it does * fit. This only applies to actions that allow PIECEMEAL * commands. * * While reducing slowly takes a bit of compute time to get * things just right, it's worth it to get as close to MAXLINE * as possible, because launching the commands we're executing * is likely to be much more compute intensive! * * Note we loop through at least once, for sourceless actions. * * Max line length is the action specific maxline or, if not * given or bigger than MAXLINE, MAXLINE. */ start = 0; chunk = length = list_length( ns ); /* commented out so jamgram.y can compile #ifdef OPT_ACTION_MAXTARGETS_EXT */ maxline = rule->maxline; /* commented so jamgram.y can compile #else maxline = rule->flags / RULE_MAXLINE; #endif */ #ifdef OPT_PIECEMEAL_PUNT_EXT maxline = maxline && maxline < CMDBUF ? maxline : CMDBUF; #else maxline = maxline && maxline < MAXLINE ? maxline : MAXLINE; #endif do { /* Build cmd: cmd_new consumes its lists. */ /* commented out so jamgram.y can compile #ifdef OPT_ACTION_MAXTARGETS_EXT */ int thischunk = rule->maxtargets != 0 ? (chunk < rule->maxtargets ? chunk : rule->maxtargets) : chunk; CMD *cmd = cmd_new( rule, list_copy( L0, nt ), list_sublist( ns, start, thischunk ), list_copy( L0, shell ), maxline ); /* commented so jamgram.y can compile #else CMD *cmd = cmd_new( rule, list_copy( L0, nt ), list_sublist( ns, start, chunk ), list_copy( L0, shell ), maxline ); #endif */ if( cmd ) { /* It fit: chain it up. */ if( !cmds ) cmds = cmd; else cmds->tail->next = cmd; cmds->tail = cmd; /* commented out so jamgram.y can compile #ifdef OPT_ACTION_MAXTARGETS_EXT */ start += thischunk; /* commented out so jamgram.y can compile #else start += chunk; #endif */ } else if( ( rule->flags & RULE_PIECEMEAL ) && chunk > 1 ) { /* Reduce chunk size slowly. */ chunk = chunk * 9 / 10; } else { /* Too long and not splittable. */ #ifdef OPT_PIECEMEAL_PUNT_EXT if (maxline < CMDBUF) { maxline = CMDBUF; continue; } #endif printf( "%s actions too long (max %d)!\n", rule->name, maxline ); exit( EXITBAD ); } } while( start < length ); /* These were always copied when used. */ list_free( nt ); list_free( ns ); /* Free the variables whose values were bound by */ /* 'actions xxx bind vars' */ popsettings( boundvars ); freesettings( boundvars ); popsettings( t->settings ); for ( autot = autosettingsreverse; autot; autot = autot->next ) { if ( autot->target != t ) pushsettings( autot->target->settings ); } } return cmds; }
static CMD * make1cmds( TARGET * t ) { CMD * cmds = 0; CMD * last_cmd; LIST * shell = L0; module_t * settings_module = 0; TARGET * settings_target = 0; ACTIONS * a0; int const running_flag = globs.noexec ? A_RUNNING_NOEXEC : A_RUNNING; /* Step through actions. */ for ( a0 = t->actions; a0; a0 = a0->next ) { RULE * rule = a0->action->rule; rule_actions * actions = rule->actions; SETTINGS * boundvars; LIST * nt; LIST * ns; ACTIONS * a1; /* Only do rules with commands to execute. */ if ( !actions ) continue; if ( a0->action->running >= running_flag ) { CMD * first; /* If this action was skipped either because it was * combined with another action by RULE_TOGETHER, or * because all of its sources were filtered out, * then we don't have anything to do here. */ if ( a0->action->first_cmd == NULL ) continue; /* This action has already been processed for another target. * Just set up the dependency graph correctly and move on. */ first = a0->action->first_cmd; if( cmds ) { last_cmd->next = cmdlist_append_cmd( last_cmd->next, first ); } else { cmds = first; } last_cmd = a0->action->last_cmd; continue; } a0->action->running = running_flag; /* Make LISTS of targets and sources. If `execute together` has been * specified for this rule, tack on sources from each instance of this * rule for this target. */ nt = make1list( L0, a0->action->targets, 0 ); ns = make1list( L0, a0->action->sources, actions->flags ); if ( actions->flags & RULE_TOGETHER ) for ( a1 = a0->next; a1; a1 = a1->next ) if ( a1->action->rule == rule && a1->action->running < running_flag && targets_equal( a0->action->targets, a1->action->targets ) ) { ns = make1list( ns, a1->action->sources, actions->flags ); a1->action->running = running_flag; } /* If doing only updated (or existing) sources, but none have been * updated (or exist), skip this action. */ if ( list_empty( ns ) && ( actions->flags & ( RULE_NEWSRCS | RULE_EXISTING ) ) ) { list_free( nt ); continue; } swap_settings( &settings_module, &settings_target, rule->module, t ); if ( list_empty( shell ) ) { /* shell is per-target */ shell = var_get( rule->module, constant_JAMSHELL ); } /* If we had 'actions xxx bind vars' we bind the vars now. */ boundvars = make1settings( rule->module, actions->bindlist ); pushsettings( rule->module, boundvars ); /* * Build command, starting with all source args. * * For actions that allow PIECEMEAL commands, if the constructed command * string is too long, we retry constructing it with a reduced number of * source arguments presented. * * While reducing slowly takes a bit of compute time to get things just * right, it is worth it to get as close to maximum allowed command * string length as possible, because launching the commands we are * executing is likely to be much more compute intensive. * * Note that we loop through at least once, for sourceless actions. */ { int const length = list_length( ns ); int start = 0; int chunk = length; int cmd_count = 0; LIST * cmd_targets = L0; LIST * cmd_shell = L0; TARGETS * semaphores = NULL; TARGETS * targets_iter; int unique_targets; do { CMD * cmd; int cmd_check_result; int cmd_error_length; int cmd_error_max_length; int retry = 0; int accept_command = 0; /* Build cmd: cmd_new() takes ownership of its lists. */ if ( list_empty( cmd_targets ) ) cmd_targets = list_copy( nt ); if ( list_empty( cmd_shell ) ) cmd_shell = list_copy( shell ); cmd = cmd_new( rule, cmd_targets, list_sublist( ns, start, chunk ), cmd_shell ); cmd_check_result = exec_check( cmd->buf, &cmd->shell, &cmd_error_length, &cmd_error_max_length ); if ( cmd_check_result == EXEC_CHECK_OK ) { accept_command = 1; } else if ( cmd_check_result == EXEC_CHECK_NOOP ) { accept_command = 1; cmd->noop = 1; } else if ( ( actions->flags & RULE_PIECEMEAL ) && ( chunk > 1 ) ) { /* Too long but splittable. Reduce chunk size slowly and * retry. */ assert( cmd_check_result == EXEC_CHECK_TOO_LONG || cmd_check_result == EXEC_CHECK_LINE_TOO_LONG ); chunk = chunk * 9 / 10; retry = 1; } else { /* Too long and not splittable. */ char const * const error_message = cmd_check_result == EXEC_CHECK_TOO_LONG ? "is too long" : "contains a line that is too long"; assert( cmd_check_result == EXEC_CHECK_TOO_LONG || cmd_check_result == EXEC_CHECK_LINE_TOO_LONG ); printf( "%s action %s (%d, max %d):\n", object_str( rule->name ), error_message, cmd_error_length, cmd_error_max_length ); /* Tell the user what did not fit. */ fputs( cmd->buf->value, stdout ); exit( EXITBAD ); } assert( !retry || !accept_command ); if ( accept_command ) { /* Chain it up. */ if ( cmds ) { last_cmd->next = cmdlist_append_cmd( last_cmd->next, cmd ); last_cmd = cmd; } else { cmds = last_cmd = cmd; } if ( cmd_count++ == 0 ) { a0->action->first_cmd = cmd; } /* Mark lists we need recreated for the next command since * they got consumed by the cmd object. */ cmd_targets = L0; cmd_shell = L0; } else { /* We can reuse targets & shell lists for the next command * if we do not let them die with this cmd object. */ cmd_release_targets_and_shell( cmd ); cmd_free( cmd ); } if ( !retry ) start += chunk; } while ( start < length ); /* Record the end of the actions cmds */ a0->action->last_cmd = last_cmd; unique_targets = 0; for ( targets_iter = a0->action->targets; targets_iter; targets_iter = targets_iter->next ) { if ( targets_contains( targets_iter->next, targets_iter->target ) ) continue; /* Add all targets produced by the action to the update list. */ push_state( &state_stack, targets_iter->target, NULL, T_STATE_MAKE1A ); ++unique_targets; } /* We need to wait until all the targets agree that * it's okay to run this action. */ ( ( CMD * )a0->action->first_cmd )->asynccnt = unique_targets; #if OPT_SEMAPHORE /* Collect semaphores */ for ( targets_iter = a0->action->targets; targets_iter; targets_iter = targets_iter->next ) { TARGET * sem = targets_iter->target->semaphore; if ( sem ) { TARGETS * semiter; if ( ! targets_contains( semaphores, sem ) ) semaphores = targetentry( semaphores, sem ); } } ( ( CMD * )a0->action->first_cmd )->lock = semaphores; ( ( CMD * )a0->action->last_cmd )->unlock = semaphores; #endif } /* These were always copied when used. */ list_free( nt ); list_free( ns ); /* Free variables with values bound by 'actions xxx bind vars'. */ popsettings( rule->module, boundvars ); freesettings( boundvars ); } if ( cmds ) { last_cmd->next = cmdlist_append_target( last_cmd->next, t ); } swap_settings( &settings_module, &settings_target, 0, 0 ); return cmds; }
int cmd_run(struct server *s, struct evhttp_request *rq, const char *uri, size_t uri_len, const char *body, size_t body_len) { char *qmark = strchr(uri, '?'); char *slash; const char *p; int cmd_len; int param_count = 0, cur_param = 1, i; struct cmd *cmd; formatting_fun f_format; /* count arguments */ if(qmark) { uri_len = qmark - uri; } for(p = uri; p && p < uri + uri_len; param_count++) { p = strchr(p+1, '/'); } if(body && body_len) { /* PUT request */ param_count++; } cmd = cmd_new(rq, param_count); /* parse URI parameters */ evhttp_parse_query(uri, &cmd->uri_params); /* get output formatting function */ uri_len = cmd_select_format(cmd, uri, uri_len, &f_format); /* check if we only have one command or more. */ slash = memchr(uri, '/', uri_len); if(slash) { cmd_len = slash - uri; } else { cmd_len = uri_len; } /* there is always a first parameter, it's the command name */ cmd->argv[0] = uri; cmd->argv_len[0] = cmd_len; /* check that the client is able to run this command */ if(!acl_allow_command(cmd, s->cfg, rq)) { return -1; } /* check if we have to split the connection */ if(cmd_is_subscribe(cmd)) { struct pubsub_client *ps; ps = calloc(1, sizeof(struct pubsub_client)); ps->s = s = server_copy(s); ps->cmd = cmd; ps->rq = rq; evhttp_connection_set_closecb(rq->evcon, on_http_disconnect, ps); } /* no args (e.g. INFO command) */ if(!slash) { redisAsyncCommandArgv(s->ac, f_format, cmd, 1, cmd->argv, cmd->argv_len); return 0; } p = slash + 1; while(p < uri + uri_len) { const char *arg = p; int arg_len; char *next = strchr(arg, '/'); if(!next || next > uri + uri_len) { /* last argument */ p = uri + uri_len; arg_len = p - arg; } else { /* found a slash */ arg_len = next - arg; p = next + 1; } /* record argument */ cmd->argv[cur_param] = decode_uri(arg, arg_len, &cmd->argv_len[cur_param], 1); cur_param++; } if(body && body_len) { /* PUT request */ cmd->argv[cur_param] = body; cmd->argv_len[cur_param] = body_len; } /* push command to Redis. */ redisAsyncCommandArgv(s->ac, f_format, cmd, cmd->count, cmd->argv, cmd->argv_len); for(i = 1; i < cur_param; ++i) { free((char*)cmd->argv[i]); } return 0; }