smcp_status_t plugtest_separate_handler( smcp_node_t node ) { struct smcp_async_response_s* async_response = NULL; smcp_transaction_t transaction = NULL; smcp_status_t ret = SMCP_STATUS_NOT_ALLOWED; smcp_method_t method = smcp_inbound_get_code(); if(method==COAP_METHOD_GET) { if(smcp_inbound_is_dupe()) { smcp_outbound_begin_response(COAP_CODE_EMPTY); smcp_outbound_send(); ret = SMCP_STATUS_OK; goto bail; } async_response = calloc(sizeof(struct smcp_async_response_s),1); if(!async_response) { ret = SMCP_STATUS_MALLOC_FAILURE; goto bail; } #if !SMCP_AVOID_PRINTF || VERBOSE_DEBUG printf("This request needs an async response. %p\n",async_response); #endif transaction = smcp_transaction_init( NULL, SMCP_TRANSACTION_DELAY_START|SMCP_TRANSACTION_ALWAYS_INVALIDATE, &plugtest_separate_async_resend_response, &plugtest_separate_async_ack_handler, (void*)async_response ); if(!transaction) { free(async_response); // TODO: Consider dropping instead...? ret = SMCP_STATUS_MALLOC_FAILURE; goto bail; } ret = smcp_transaction_begin( smcp_get_current_instance(), transaction, (smcp_inbound_get_packet()->tt==COAP_TRANS_TYPE_CONFIRMABLE)?(smcp_cms_t)(COAP_MAX_TRANSMIT_WAIT*MSEC_PER_SEC):1 ); if(SMCP_STATUS_OK != ret) { smcp_transaction_end(smcp_get_current_instance(),transaction); goto bail; } ret = smcp_start_async_response(async_response,0); if(ret) { goto bail; } async_response = NULL; } bail: return ret; }
smcp_status_t async_request_handler( smcp_node_t node ) { smcp_status_t ret = SMCP_STATUS_OK; smcp_method_t method = smcp_inbound_get_code(); if(method==COAP_METHOD_GET) { ret = smcp_start_async_response(&async_response,0); if(ret==SMCP_STATUS_DUPE) { printf(" ** Dupe, already preparing async response.\n"); } else if(!ret) { printf(" *** Request requires an async response...!\n"); } require_noerr(ret, bail); ret = smcp_begin_transaction_old( smcp_get_current_instance(), smcp_get_next_msg_id(smcp_get_current_instance()), 30*1000, // Retry for thirty seconds. SMCP_TRANSACTION_DELAY_START, // Flags (void*)&resend_async_response, (void*)&async_response_ack_handler, (void*)&async_response ); } else { ret = SMCP_STATUS_NOT_ALLOWED; } bail: return ret; }
smcp_status_t smcp_observable_update(smcp_observable_t context, uint8_t key) { smcp_status_t ret = SMCP_STATUS_OK; smcp_t const interface = smcp_get_current_instance(); int8_t i; #if !SMCP_EMBEDDED context->interface = interface; #endif if(interface->inbound.is_fake || interface->inbound.is_dupe) { goto bail; } for(i = context->first_observer-1; i >= 0; i = observer_table[i].next - 1) { if(observer_table[i].key != key) continue; if(smcp_inbound_is_related_to_async_response(&observer_table[i].async_response)) break; } if(interface->inbound.has_observe_option) { if(i == -1) { i = get_unused_observer_index(); if(i == -1) goto bail; if(context->last_observer == 0) { context->first_observer = context->last_observer = i + 1; } else { observer_table[context->last_observer-1].next = i +1; context->last_observer = i + 1; } observer_table[i].key = key; observer_table[i].seq = 0; observer_table[i].observable = context; } smcp_start_async_response(&observer_table[i].async_response,SMCP_ASYNC_RESPONSE_FLAG_DONT_ACK); ret = smcp_outbound_add_option_uint(COAP_OPTION_OBSERVE,observer_table[i].seq); } else if(i != -1) { free_observer(&observer_table[i]); } bail: return ret; }
smcp_status_t smcp_curl_proxy_request_handler( smcp_curl_proxy_node_t node ) { smcp_status_t ret = SMCP_STATUS_NOT_ALLOWED; smcp_curl_request_t request = NULL; struct curl_slist *headerlist=NULL; smcp_method_t method = smcp_inbound_get_code(); //require_action(method<=COAP_METHOD_DELETE,bail,ret = SMCP_STATUS_NOT_ALLOWED); //require_action(COAP_OPTION_URI_PATH!=smcp_inbound_peek_option(NULL,NULL),bail,ret=SMCP_STATUS_NOT_FOUND); node->interface = smcp_get_current_instance(); smcp_inbound_reset_next_option(); request = smcp_curl_request_create(); request->proxy_node = node; require_action(request!=NULL,bail,ret = SMCP_STATUS_MALLOC_FAILURE); switch(method) { case COAP_METHOD_GET: curl_easy_setopt(request->curl, CURLOPT_CUSTOMREQUEST, "GET"); break; case COAP_METHOD_PUT: curl_easy_setopt(request->curl, CURLOPT_PUT, 1L); break; case COAP_METHOD_POST: curl_easy_setopt(request->curl, CURLOPT_POST, 1L); break; case COAP_METHOD_DELETE: curl_easy_setopt(request->curl, CURLOPT_CUSTOMREQUEST, "DELETE"); break; default: ret = SMCP_STATUS_NOT_ALLOWED; break; } { coap_option_key_t key; const uint8_t* value; coap_size_t value_len; while((key=smcp_inbound_next_option(&value, &value_len))!=COAP_OPTION_INVALID) { if(key==COAP_OPTION_PROXY_URI) { char uri[value_len+1]; memcpy(uri,value,value_len); uri[value_len] = 0; curl_easy_setopt(request->curl, CURLOPT_URL, uri); assert_printf("CuRL URL: \"%s\"",uri); ret = 0; } else if(key==COAP_OPTION_URI_HOST) { } else if(key==COAP_OPTION_URI_PORT) { } else if(key==COAP_OPTION_URI_PATH) { } else if(key==COAP_OPTION_URI_QUERY) { } else if(key==COAP_OPTION_CONTENT_TYPE || key==COAP_OPTION_ACCEPT) { const char* option_name = coap_option_key_to_cstr(key, false); const char* value_string = coap_content_type_to_cstr(value[1]); char header[strlen(option_name)+strlen(value_string)+3]; strcpy(header,option_name); strcat(header,": "); strcat(header,value_string); headerlist = curl_slist_append(headerlist, header); assert_printf("CuRL HEADER: \"%s\"",header); } else { if(coap_option_value_is_string(key)) { const char* option_name = coap_option_key_to_cstr(key, false); char header[strlen(option_name)+value_len+3]; strcpy(header,option_name); strcat(header,": "); strncat(header,(const char*)value,value_len); assert_printf("CuRL HEADER: \"%s\"",header); headerlist = curl_slist_append(headerlist, header); } } } } require_noerr(ret,bail); if(smcp_inbound_get_content_len()) { coap_size_t len = smcp_inbound_get_content_len(); request->output_content = calloc(1,len+1); request->output_content_len = len; memcpy(request->output_content,smcp_inbound_get_content_ptr(),len); curl_easy_setopt(request->curl, CURLOPT_READFUNCTION, ReadMemoryCallback); curl_easy_setopt(request->curl, CURLOPT_READDATA, (void *)request); } curl_easy_setopt(request->curl, CURLOPT_USERAGENT, "smcp-curl-proxy/1.0"); curl_easy_setopt(request->curl, CURLOPT_HTTPHEADER, headerlist),headerlist=NULL; curl_easy_setopt(request->curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); curl_easy_setopt(request->curl, CURLOPT_WRITEDATA, (void *)request); ret = smcp_start_async_response(&request->async_response,0); require_noerr(ret,bail); if(node->curl_multi_handle) curl_multi_add_handle(node->curl_multi_handle, request->curl); else curl_easy_perform(request->curl); bail: if(headerlist) curl_slist_free_all(headerlist); if(ret && request) smcp_curl_request_release(request); return ret; }
smcp_status_t cgi_node_request_change_state(cgi_node_t node, cgi_node_request_t request, cgi_node_state_t new_state) { // TODO: Possibly do more later...? syslog(LOG_INFO, "cgi-node: %d -> %d", request->state, new_state); if(request->state == new_state) { // Same state, do nothing. } else if ( request->state == CGI_NODE_STATE_INACTIVE && new_state == CGI_NODE_STATE_ACTIVE_BLOCK1_WAIT_REQ ) { } else if ( request->state == CGI_NODE_STATE_ACTIVE_BLOCK1_WAIT_REQ && new_state == CGI_NODE_STATE_ACTIVE_BLOCK1_WAIT_FD ) { smcp_start_async_response(&request->async_response, 0); } else if ( request->state == CGI_NODE_STATE_ACTIVE_BLOCK1_WAIT_FD && new_state == CGI_NODE_STATE_ACTIVE_BLOCK1_WAIT_ACK ) { cgi_node_send_next_block(node,request); } else if ( request->state == CGI_NODE_STATE_ACTIVE_BLOCK1_WAIT_ACK && new_state == CGI_NODE_STATE_ACTIVE_BLOCK1_WAIT_REQ ) { if (request->transaction) { smcp_transaction_end(smcp_get_current_instance(),request->transaction); request->transaction = NULL; } } else if ( ( (request->state == CGI_NODE_STATE_ACTIVE_BLOCK2_WAIT_REQ) || (request->state == CGI_NODE_STATE_ACTIVE_BLOCK1_WAIT_REQ) ) && ( new_state == CGI_NODE_STATE_ACTIVE_BLOCK2_WAIT_FD) ) { if(!request->stdin_buffer_len && request->fd_cmd_stdin>=0) { close(request->fd_cmd_stdin); request->fd_cmd_stdin = -1; } smcp_start_async_response(&request->async_response, 0); } else if(request->state == CGI_NODE_STATE_ACTIVE_BLOCK2_WAIT_FD && new_state == CGI_NODE_STATE_ACTIVE_BLOCK2_WAIT_ACK ) { if(!request->stdin_buffer_len && request->fd_cmd_stdin>=0) { close(request->fd_cmd_stdin); request->fd_cmd_stdin = -1; } cgi_node_send_next_block(node,request); } else if(request->state == CGI_NODE_STATE_ACTIVE_BLOCK2_WAIT_ACK && new_state == CGI_NODE_STATE_ACTIVE_BLOCK2_WAIT_REQ ) { if(!request->stdin_buffer_len && request->fd_cmd_stdin>=0) { close(request->fd_cmd_stdin); request->fd_cmd_stdin = -1; } //cgi_node_request_pop_bytes_from_stdout(request,(1<<((request->block2&0x7)+4))); if(request->transaction) { smcp_transaction_end(smcp_get_current_instance(),request->transaction); request->transaction = NULL; } } else if(new_state == CGI_NODE_STATE_FINISHED) { if(request->transaction) { smcp_transaction_end(smcp_get_current_instance(),request->transaction); request->transaction = NULL; } close(request->fd_cmd_stdin); request->fd_cmd_stdin = -1; close(request->fd_cmd_stdout); request->fd_cmd_stdout = -1; if(request->pid != 0 && request->pid != -1) { int status; kill(request->pid,SIGTERM); if(waitpid(request->pid, &status, WNOHANG) == request->pid) { request->pid = 0; } } } else { // INVALID STATE TRANSITION! syslog(LOG_ERR, "cgi-node: BAD STATE CHANGE: %d -> %d", request->state, new_state); abort(); } request->state = new_state; return SMCP_STATUS_OK; }
cgi_node_request_t cgi_node_create_request(cgi_node_t node) { cgi_node_request_t ret = NULL; int i; int pipe_cmd_stdin[2]; int pipe_cmd_stdout[2]; for (i=0; i < CGI_NODE_MAX_REQUESTS; i++) { if (node->requests[i].state <= CGI_NODE_STATE_FINISHED) { ret = &node->requests[i]; if (node->requests[i].state == CGI_NODE_STATE_INACTIVE) { break; } } } require(ret,bail); ret->is_active = 1; ret->state = CGI_NODE_STATE_ACTIVE_BLOCK1_WAIT_REQ; if(ret->pid != 0 && ret->pid != -1) { int status; kill(ret->pid,SIGKILL); waitpid(ret->pid, &status, 0); } if(ret->fd_cmd_stdin>=0) { close(ret->fd_cmd_stdin); } if(ret->fd_cmd_stdout>=0) { close(ret->fd_cmd_stdout); } ret->pid = 0; ret->fd_cmd_stdin = -1; ret->fd_cmd_stdout = -1; ret->block1 = BLOCK_OPTION_UNSPECIFIED; ret->block2 = BLOCK_OPTION_DEFAULT; // Default value, overwrite with actual block ret->stdin_buffer_len = 0; ret->stdout_buffer_len = 0; ret->expiration = smcp_plat_cms_to_timestamp(30 * MSEC_PER_SEC); free(ret->stdin_buffer); ret->stdin_buffer = NULL; free(ret->stdout_buffer); ret->stdout_buffer = NULL; smcp_start_async_response(&ret->async_response, SMCP_ASYNC_RESPONSE_FLAG_DONT_ACK); pipe(pipe_cmd_stdin); pipe(pipe_cmd_stdout); if(!(ret->pid=fork())) { // We are the child! char path[2048]; // todo: this max should be a preprocessor macro // Update stdin and stdout. dup2(pipe_cmd_stdin[0],STDIN_FILENO); dup2(pipe_cmd_stdout[1],STDOUT_FILENO); close(pipe_cmd_stdin[0]); close(pipe_cmd_stdin[1]); close(pipe_cmd_stdout[0]); close(pipe_cmd_stdout[1]); path[0] = 0; smcp_node_get_path(&node->node,path,sizeof(path)); setenv("SCRIPT_NAME",path,1); setenv("SERVER_SOFTWARE","smcpd/"PACKAGE_VERSION,1); setenv("REQUEST_METHOD",coap_code_to_cstr(smcp_inbound_get_packet()->code),1); setenv("REQUEST_URI",smcp_inbound_get_path(path,2),1); setenv("GATEWAY_INTERFACE","CGI/1.1",1); setenv("SERVER_PROTOCOL","CoAP/1.0",1); setenv("REMOTE_ADDR","",1); setenv("REMOTE_PORT","",1); setenv("SERVER_NAME","",1); setenv("SERVER_ADDR","",1); setenv("SERVER_PORT","",1); if (0 == strncmp(path,getenv("SCRIPT_NAME"),strlen(getenv("SCRIPT_NAME")))) { setenv("PATH_INFO",path+strlen(getenv("SCRIPT_NAME")),1); } syslog(LOG_DEBUG, "cgi-node: About to execute \"%s\" using shell \"%s\"", node->cmd, node->shell); execl(node->shell,node->shell,"-c",node->cmd,NULL); syslog(LOG_ERR, "cgi-node: Failed to execute \"%s\" using shell \"%s\"", node->cmd, node->shell); // We should never get here... abort(); } if (ret->pid < 0) { // Oh hell. syslog(LOG_ERR,"Unable to fork!"); close(pipe_cmd_stdin[0]); close(pipe_cmd_stdin[1]); close(pipe_cmd_stdout[0]); close(pipe_cmd_stdout[1]); ret->is_active = 0; ret->state = CGI_NODE_STATE_INACTIVE; ret = NULL; goto bail; } ret->fd_cmd_stdin = pipe_cmd_stdin[1]; ret->fd_cmd_stdout = pipe_cmd_stdout[0]; close(pipe_cmd_stdin[0]); close(pipe_cmd_stdout[1]); bail: return ret; }