void my_free(void *ptr) { header_t *header; if (ptr) { header = (header_t *)((UINTPTR_T)ptr - sizeof (header_t)); add_to_list(header, header->list); merge_headers(header->list); release_pages(header->list); } }
char * passenger_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) { passenger_loc_conf_t *prev = parent; passenger_loc_conf_t *conf = child; ngx_http_core_loc_conf_t *clcf; size_t size; ngx_hash_init_t hash; clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); if (generated_merge_part(conf, prev, cf) == 0) { return NGX_CONF_ERROR; } if (prev->options_cache.data == NULL) { if (cache_loc_conf_options(cf, prev) != NGX_OK) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "cannot create " PROGRAM_NAME " configuration cache"); return NGX_CONF_ERROR; } } /******************************/ /******************************/ #if (NGX_HTTP_CACHE) && NGINX_VERSION_NUM >= 1007009 if (conf->upstream_config.store > 0) { conf->upstream_config.cache = 0; } if (conf->upstream_config.cache > 0) { conf->upstream_config.store = 0; } #endif #if NGINX_VERSION_NUM >= 1007009 if (conf->upstream_config.store == NGX_CONF_UNSET) { ngx_conf_merge_value(conf->upstream_config.store, prev->upstream_config.store, 0); conf->upstream_config.store_lengths = prev->upstream_config.store_lengths; conf->upstream_config.store_values = prev->upstream_config.store_values; } #else if (conf->upstream_config.store != 0) { ngx_conf_merge_value(conf->upstream_config.store, prev->upstream_config.store, 0); if (conf->upstream_config.store_lengths == NULL) { conf->upstream_config.store_lengths = prev->upstream_config.store_lengths; conf->upstream_config.store_values = prev->upstream_config.store_values; } } #endif ngx_conf_merge_uint_value(conf->upstream_config.store_access, prev->upstream_config.store_access, 0600); #if NGINX_VERSION_NUM >= 1007005 ngx_conf_merge_uint_value(conf->upstream_config.next_upstream_tries, prev->upstream_config.next_upstream_tries, 0); #endif ngx_conf_merge_value(conf->upstream_config.buffering, prev->upstream_config.buffering, 0); ngx_conf_merge_value(conf->upstream_config.ignore_client_abort, prev->upstream_config.ignore_client_abort, 0); #if NGINX_VERSION_NUM >= 1007007 ngx_conf_merge_value(conf->upstream_config.force_ranges, prev->upstream_config.force_ranges, 0); #endif ngx_conf_merge_ptr_value(conf->upstream_config.local, prev->upstream_config.local, NULL); ngx_conf_merge_msec_value(conf->upstream_config.connect_timeout, prev->upstream_config.connect_timeout, 12000000); ngx_conf_merge_msec_value(conf->upstream_config.send_timeout, prev->upstream_config.send_timeout, 12000000); ngx_conf_merge_msec_value(conf->upstream_config.read_timeout, prev->upstream_config.read_timeout, 12000000); #if NGINX_VERSION_NUM >= 1007005 ngx_conf_merge_msec_value(conf->upstream_config.next_upstream_timeout, prev->upstream_config.next_upstream_timeout, 0); #endif ngx_conf_merge_size_value(conf->upstream_config.send_lowat, prev->upstream_config.send_lowat, 0); ngx_conf_merge_size_value(conf->upstream_config.buffer_size, prev->upstream_config.buffer_size, 16 * 1024); #if NGINX_VERSION_NUM >= 1007007 ngx_conf_merge_size_value(conf->upstream_config.limit_rate, prev->upstream_config.limit_rate, 0); #endif ngx_conf_merge_bufs_value(conf->upstream_config.bufs, prev->upstream_config.bufs, 8, 16 * 1024); if (conf->upstream_config.bufs.num < 2) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "there must be at least 2 \"passenger_buffers\""); return NGX_CONF_ERROR; } size = conf->upstream_config.buffer_size; if (size < conf->upstream_config.bufs.size) { size = conf->upstream_config.bufs.size; } ngx_conf_merge_size_value(conf->upstream_config.busy_buffers_size_conf, prev->upstream_config.busy_buffers_size_conf, NGX_CONF_UNSET_SIZE); if (conf->upstream_config.busy_buffers_size_conf == NGX_CONF_UNSET_SIZE) { conf->upstream_config.busy_buffers_size = 2 * size; } else { conf->upstream_config.busy_buffers_size = conf->upstream_config.busy_buffers_size_conf; } if (conf->upstream_config.busy_buffers_size < size) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"passenger_busy_buffers_size\" must be equal to or greater " "than the maximum of the value of \"passenger_buffer_size\" and " "one of the \"passenger_buffers\""); return NGX_CONF_ERROR; } if (conf->upstream_config.busy_buffers_size > (conf->upstream_config.bufs.num - 1) * conf->upstream_config.bufs.size) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"passenger_busy_buffers_size\" must be less than " "the size of all \"passenger_buffers\" minus one buffer"); return NGX_CONF_ERROR; } ngx_conf_merge_size_value(conf->upstream_config.temp_file_write_size_conf, prev->upstream_config.temp_file_write_size_conf, NGX_CONF_UNSET_SIZE); if (conf->upstream_config.temp_file_write_size_conf == NGX_CONF_UNSET_SIZE) { conf->upstream_config.temp_file_write_size = 2 * size; } else { conf->upstream_config.temp_file_write_size = conf->upstream_config.temp_file_write_size_conf; } if (conf->upstream_config.temp_file_write_size < size) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"passenger_temp_file_write_size\" must be equal to or greater than " "the maximum of the value of \"passenger_buffer_size\" and " "one of the \"passenger_buffers\""); return NGX_CONF_ERROR; } ngx_conf_merge_size_value(conf->upstream_config.max_temp_file_size_conf, prev->upstream_config.max_temp_file_size_conf, NGX_CONF_UNSET_SIZE); if (conf->upstream_config.max_temp_file_size_conf == NGX_CONF_UNSET_SIZE) { conf->upstream_config.max_temp_file_size = 1024 * 1024 * 1024; } else { conf->upstream_config.max_temp_file_size = conf->upstream_config.max_temp_file_size_conf; } if (conf->upstream_config.max_temp_file_size != 0 && conf->upstream_config.max_temp_file_size < size) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"passenger_max_temp_file_size\" must be equal to zero to disable " "temporary files usage or must be equal to or greater than " "the maximum of the value of \"passenger_buffer_size\" and " "one of the \"passenger_buffers\""); return NGX_CONF_ERROR; } ngx_conf_merge_bitmask_value(conf->upstream_config.ignore_headers, prev->upstream_config.ignore_headers, NGX_CONF_BITMASK_SET); ngx_conf_merge_bitmask_value(conf->upstream_config.next_upstream, prev->upstream_config.next_upstream, (NGX_CONF_BITMASK_SET |NGX_HTTP_UPSTREAM_FT_ERROR |NGX_HTTP_UPSTREAM_FT_TIMEOUT)); if (conf->upstream_config.next_upstream & NGX_HTTP_UPSTREAM_FT_OFF) { conf->upstream_config.next_upstream = NGX_CONF_BITMASK_SET |NGX_HTTP_UPSTREAM_FT_OFF; } ngx_conf_merge_path_value(cf, &conf->upstream_config.temp_path, prev->upstream_config.temp_path, &ngx_http_proxy_temp_path); #if (NGX_HTTP_CACHE) #if NGINX_VERSION_NUM >= 1007009 if (conf->upstream_config.cache == NGX_CONF_UNSET) { ngx_conf_merge_value(conf->upstream_config.cache, prev->upstream_config.cache, 0); conf->upstream_config.cache_zone = prev->upstream_config.cache_zone; conf->upstream_config.cache_value = prev->upstream_config.cache_value; } if (conf->upstream_config.cache_zone && conf->upstream_config.cache_zone->data == NULL) { ngx_shm_zone_t *shm_zone; shm_zone = conf->upstream_config.cache_zone; ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"scgi_cache\" zone \"%V\" is unknown", &shm_zone->shm.name); return NGX_CONF_ERROR; } #else ngx_conf_merge_ptr_value(conf->upstream_config.cache, prev->upstream_config.cache, NULL); if (conf->upstream_config.cache && conf->upstream_config.cache->data == NULL) { ngx_shm_zone_t *shm_zone; shm_zone = conf->upstream_config.cache; ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"scgi_cache\" zone \"%V\" is unknown", &shm_zone->shm.name); return NGX_CONF_ERROR; } #endif ngx_conf_merge_uint_value(conf->upstream_config.cache_min_uses, prev->upstream_config.cache_min_uses, 1); ngx_conf_merge_bitmask_value(conf->upstream_config.cache_use_stale, prev->upstream_config.cache_use_stale, (NGX_CONF_BITMASK_SET | NGX_HTTP_UPSTREAM_FT_OFF)); if (conf->upstream_config.cache_use_stale & NGX_HTTP_UPSTREAM_FT_OFF) { conf->upstream_config.cache_use_stale = NGX_CONF_BITMASK_SET | NGX_HTTP_UPSTREAM_FT_OFF; } if (conf->upstream_config.cache_use_stale & NGX_HTTP_UPSTREAM_FT_ERROR) { conf->upstream_config.cache_use_stale |= NGX_HTTP_UPSTREAM_FT_NOLIVE; } if (conf->upstream_config.cache_methods == 0) { conf->upstream_config.cache_methods = prev->upstream_config.cache_methods; } conf->upstream_config.cache_methods |= NGX_HTTP_GET | NGX_HTTP_HEAD; ngx_conf_merge_ptr_value(conf->upstream_config.cache_bypass, prev->upstream_config.cache_bypass, NULL); ngx_conf_merge_ptr_value(conf->upstream_config.no_cache, prev->upstream_config.no_cache, NULL); ngx_conf_merge_ptr_value(conf->upstream_config.cache_valid, prev->upstream_config.cache_valid, NULL); if (conf->cache_key.value.data == NULL) { conf->cache_key = prev->cache_key; } ngx_conf_merge_value(conf->upstream_config.cache_lock, prev->upstream_config.cache_lock, 0); ngx_conf_merge_msec_value(conf->upstream_config.cache_lock_timeout, prev->upstream_config.cache_lock_timeout, 5000); ngx_conf_merge_value(conf->upstream_config.cache_revalidate, prev->upstream_config.cache_revalidate, 0); #if NGINX_VERSION_NUM >= 1007008 ngx_conf_merge_msec_value(conf->upstream_config.cache_lock_age, prev->upstream_config.cache_lock_age, 5000); #endif #if NGINX_VERSION_NUM >= 1006000 ngx_conf_merge_value(conf->upstream_config.cache_revalidate, prev->upstream_config.cache_revalidate, 0); #endif #endif ngx_conf_merge_value(conf->upstream_config.pass_request_headers, prev->upstream_config.pass_request_headers, 1); ngx_conf_merge_value(conf->upstream_config.pass_request_body, prev->upstream_config.pass_request_body, 1); ngx_conf_merge_value(conf->upstream_config.intercept_errors, prev->upstream_config.intercept_errors, 0); hash.max_size = 512; hash.bucket_size = ngx_align(64, ngx_cacheline_size); hash.name = "passenger_hide_headers_hash"; if (ngx_http_upstream_hide_headers_hash(cf, &conf->upstream_config, &prev->upstream_config, headers_to_hide, &hash) != NGX_OK) { return NGX_CONF_ERROR; } if (conf->upstream_config.upstream == NULL) { conf->upstream_config.upstream = prev->upstream_config.upstream; } if (conf->enabled == 1 /* and not NGX_CONF_UNSET */ && passenger_main_conf.root_dir.len != 0 && clcf->handler == NULL /* no handler set by other modules */) { clcf->handler = passenger_content_handler; } conf->headers_hash_bucket_size = ngx_align(conf->headers_hash_bucket_size, ngx_cacheline_size); hash.max_size = conf->headers_hash_max_size; hash.bucket_size = conf->headers_hash_bucket_size; hash.name = "passenger_headers_hash"; if (merge_headers(cf, conf, prev) != NGX_OK) { return NGX_CONF_ERROR; } if (cache_loc_conf_options(cf, conf) != NGX_OK) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "cannot create " PROGRAM_NAME " configuration cache"); return NGX_CONF_ERROR; } return NGX_CONF_OK; }
/* sends a miniroute packet, automatically discovering the path if necessary. See description in the * .h file. */ int miniroute_send_pkt(network_address_t dest_address, int hdr_len, char* hdr, int data_len, char* data) { // This will store the route request struct, which is a structure related to the // search for a path to the host route_request_t route_request; // Store the routing header routing_header_t routing_header; // Store the route to the host, which is an array of addresses network_address_t* route; // Store the route data struct, which holds the route and some metadata route_data_t route_data; // Store my address network_address_t my_addr; // Used to synchronize access with structures the network handler touches interrupt_level_t prev_level; // This will store the combined routing + normal headers char* full_header; network_address_t dest_address2; // These will store data related to the routes int time_route_found; int route_len; int route_valid = 1; // Used to get data from the header containing the paht routing_header_t tmp_routing_header; // Used to just check the IP of senders; combats UDP port issues w/ simulated broadcasts unsigned int dest_address_ip = dest_address[0]; // Loop + tmp variables int current_req_id; int success = 0; int alarm_id; int x; int i; if (hdr_len == 0 || hdr == NULL || data_len == 0 || data == NULL) return -1; // Get the route item, which is a hashmap_item_t, from the hashmap for this addr semaphore_P(route_cache_sem); route_data = (route_data_t) hashmap_get(route_cache, hash_address(dest_address)); // If it's not NULL, extract the data from the item if (route_data != NULL) { time_route_found = route_data->time_found; route_len = route_data->route_len; // caveat: the cleanup thread may delete the route data, so we need to // save it in a separate variable, just incase. route = (network_address_t*) malloc(sizeof(network_address_t) * route_len); if (route == NULL) { semaphore_V(route_cache_sem); return -1; } memcpy(route, route_data->route, sizeof(network_address_t) * route_len); } else { route_valid = 0; } semaphore_V(route_cache_sem); // Check, if the route isn't NULL, if it's expired if (route_valid == 1 && (ticks - time_route_found) * PERIOD/MILLISECOND > 3000) { route_valid = 0; } // If the route is invalid (either not in the cache or expired)... if (route_valid == 0) { // We won't be needing that previous route variable if (route_data != NULL) { // But, just in case someone is still using it, use the route cache semaphore semaphore_P(route_cache_sem); free(route); semaphore_V(route_cache_sem); } // Check if someone else already initiated this route discovery request prev_level = set_interrupt_level(DISABLED); route_request = (route_request_t) hashmap_get(current_discovery_requests, dest_address_ip); set_interrupt_level(prev_level); // If so, we can just wait for their result if (route_request != NULL) { // Wait for the other thread to get the path // The threads waiting variable needs to be synchronized. We decided // to reuse the route cache sem, as there will not be much lock // contention semaphore_P(route_cache_sem); route_request->threads_waiting++; semaphore_V(route_cache_sem); semaphore_P(route_request->waiting_sem); // Get the route from the hashmap semaphore_P(route_cache_sem); route_data = (route_data_t) hashmap_get(route_cache, hash_address(dest_address)); // If the other thread didn't get the route, return an error if (route_data == NULL) { // Return failure... semaphore_V(route_cache_sem); return -1; } else { time_route_found = route_data->time_found; route_len = route_data->route_len; if ((ticks - time_route_found) * PERIOD/MILLISECOND > 3000) { // This could have been a left-over expired cache entry that we haven't // deleted yet. semaphore_V(route_cache_sem); return -1; } // Save the route in a separate variable in case the route gets cleaned up // while we're using it route = (network_address_t*) malloc(sizeof(network_address_t) * route_len); if (route == NULL) { semaphore_V(route_cache_sem); return -1; } memcpy(route, route_data->route, sizeof(network_address_t) * route_len); semaphore_V(route_cache_sem); } } else { // Otherwise, we have to do the route discovery process // Create a new route request struct route_request = create_route_request(); if (route_request == NULL) { return -1; } // Add the route request to the current discovery requests prev_level = set_interrupt_level(DISABLED); hashmap_insert(current_discovery_requests, dest_address_ip, route_request); set_interrupt_level(prev_level); // We'll try the route discovery process three times for (i = 0; i < 3; i++) { // Register an alarm to wake this thread up as it waits for a response alarm_id = register_alarm(12000, &alarm_wakeup_sem, (void*) route_request->initiator_sem); // Increment the request ID - must be synchronized, obviously semaphore_P(request_id_sem); current_req_id = route_request_id++; semaphore_V(request_id_sem); // We need to make a header for the discovery request, but the path // needs to have our address in it, so the reply can be forwarded back // to us network_get_my_address(my_addr); // Passing in the address of this local variable will suffice, as the // value is immediately copied into the header and then not used again // Create a routing header for the route discovery request routing_header = create_miniroute_header(ROUTING_ROUTE_DISCOVERY, dest_address, current_req_id, MAX_ROUTE_LENGTH, 1, &my_addr); if (routing_header == NULL) { return -1; } // Combine it with the given header full_header = merge_headers(routing_header, hdr, hdr_len); if (full_header == NULL) { free(routing_header); return -1; } // Send out the route discovery request network_bcast_pkt(sizeof(struct routing_header)+hdr_len, (char*) full_header, data_len, data); // Wait for a reply (which will be signalled by the network handler) prev_level = set_interrupt_level(DISABLED); semaphore_P(route_request->initiator_sem); set_interrupt_level(prev_level); // Check if we got a successful response if (route_request->interrupt_arg != NULL) { // Deregister the alarm before it tries to wake us up // Needs to be synchronized, as the IH touches it and we destroy it here prev_level = set_interrupt_level(alarm_id); deregister_alarm(alarm_id); set_interrupt_level(alarm_id); // Get the header tmp_routing_header = (routing_header_t) route_request->interrupt_arg->buffer; route_len = unpack_unsigned_int(tmp_routing_header->path_len); // Then the path, for our own use later in this function // We'll also create one copy and put it in the route data struct route = miniroute_reverse_raw_path(tmp_routing_header, route_len); if (route == NULL) { free(routing_header); free(full_header); return -1; } // Create a route data struct - with a different route (as it will be deleted by a diff thread) route_data = create_route_data(miniroute_reverse_raw_path(tmp_routing_header, route_len), route_len, ticks); if (route_data == NULL) { free(routing_header); free(full_header); return -1; } // add it to the cache hashmap semaphore_P(route_cache_sem); hashmap_insert(route_cache, hash_address(dest_address), route_data); semaphore_V(route_cache_sem); // Wake up the other threads waiting for (x = 0; x < route_request->threads_waiting; x++) { semaphore_V(route_request->waiting_sem); } // Clean up the route request struct, then delete it from the hashmap // DELETE ROUTE REQUEST WILL FREE THE NETWORK INTERRUPT ARG! prev_level = set_interrupt_level(DISABLED); delete_route_request(route_request); hashmap_delete(current_discovery_requests, dest_address_ip); set_interrupt_level(prev_level); // We don't need to actually get any of the routing stuff from the // route_ite, as this process also sent the data packet // Free the headers free(routing_header); free(full_header); // Return the total bytes sent, not including the routing header success = 1; break; } } // If we didn't get a successful response after 3 tries... if (success == 0) { // Wake up the other threads waiting so they can see we failed for (x = 0; x < route_request->threads_waiting; x++) { semaphore_V(route_request->waiting_sem); } // clean up the route request struct, then delete it from the hashmap prev_level = set_interrupt_level(DISABLED); delete_route_request(route_request); hashmap_delete(current_discovery_requests, dest_address_ip); set_interrupt_level(prev_level); // Free the headers free(routing_header); free(full_header); // Return failure... return -1; } } } // If we're here, we either found the route in the cache or waited for another // thread to finish getting the route (and it did so successfully) network_address_copy(route[route_len-1], dest_address2); // Need to update the dst address to deal with UDP port issues // This again is due to UDP port issues... pack_address(((mini_header_t) hdr)->destination_address, dest_address2); // Create a routing header for the data packet routing_header = create_miniroute_header(ROUTING_DATA, dest_address2, 0, MAX_ROUTE_LENGTH, route_len, route); if (routing_header == NULL) { return -1; } // Combine it with the given header full_header = merge_headers(routing_header, hdr, hdr_len); if (full_header == NULL) { free(routing_header); } // Set the right destination address network_address_copy(route[1], dest_address2); // Send the packet network_send_pkt(dest_address2, sizeof(struct routing_header) + hdr_len, full_header, data_len, data); // Free the route + headers free(route); free(routing_header); free(full_header); // Return the total data sent return hdr_len + data_len; }