void *handle_connection(void *arg) { st_netfd_t client_nfd = (st_netfd_t)arg; struct http_stream *s = http_stream_create(HTTP_SERVER, SEC2USEC(10)); char buf[4*1024]; for (;;) { if (http_stream_request_read(s, client_nfd) < 0) break; http_request_debug_print(s->req, stderr); size_t total = 0; for (;;) { ssize_t nr = sizeof(buf); int status = http_stream_read(s, buf, &nr); fprintf(stderr, "http_stream_read nr: %zd\n", nr); if (nr < 0 || status != HTTP_STREAM_OK) { goto done; } if (nr == 0) break; /*fwrite(buf, sizeof(char), nr, stdout);*/ total += nr; } fprintf(stderr, "http_stream_read total: %zu\n", total); http_response_clear(s->resp); s->resp->http_version = "HTTP/1.1"; s->resp->status_code = 200; s->resp->reason = "OK"; http_response_header_append(s->resp, "Content-type", "text/html"); /*http_response_header_append(s->resp, "Connection", "close");*/ http_response_set_body(s->resp, "<H2>It worked!</H2>"); ssize_t nw = http_stream_response_send(s, 1); fprintf(stderr, "http_stream_response_send: %zd\n", nw); /* TODO: break loop if HTTP/1.0 and not keep-alive */ } done: fprintf(stderr, "exiting handle_connection\n"); http_stream_close(s); return NULL; }
/** * Parse authentication reply radius attributes. * @param[in] session Session. * @param[in] attrs Radius attributes. * @param[in] rules Client rules. */ static void zrad_auth_parse(zsession_t *session, const VALUE_PAIR *attrs, zclient_rules_t *rules) { zclient_rules_init(rules); for (; likely(NULL != attrs); attrs = attrs->next) { switch (attrs->attribute) { case PW_FILTER_ID: zclient_rule_parse(zinst()->client_rule_parser, rules, attrs->strvalue); break; case PW_SESSION_TIMEOUT: atomic_store_release(&session->timeout, SEC2USEC(attrs->lvalue)); break; case PW_ACCT_INTERIM_INTERVAL: atomic_store_release(&session->acct_interval, SEC2USEC(attrs->lvalue)); break; case PW_IDLE_TIMEOUT: atomic_store_release(&session->idle_timeout, SEC2USEC(attrs->lvalue)); break; default: ZLOG(LOG_DEBUG, "Unknown radius attribute %d", attrs->attribute); } } }
static void zpkt_dhcp_bootreply(zpacket_t *packet, const struct dhcp_header *dhcph, size_t len) { uint32_t session_ip = ntohl(dhcph->yiaddr); packet->dst_ip = session_ip; zscope_t *scope = zpacket_guess_scope(packet); if (!scope || !scope->cfg->security.dhcp_snooping) { return; } bool is_ack = false; uint64_t lease_time = 0; const struct dhcp_opt *opt = dhcph->opts; while (len >= DHCP_OPT_SIZE(opt)) { switch (opt->code) { case DHCP_OPT_MESSAGE: if ((sizeof(uint8_t) == opt->len) && (DHCPACK == opt->data.u8[0])) { is_ack = true; } else { // skip all non-ack packets return; } break; case DHCP_OPT_LEASE_TIME: if (sizeof(uint32_t) == opt->len) { lease_time = SEC2USEC(ntohl(opt->data.u32[0])); } else { return; } break; default: break; } len -= DHCP_OPT_SIZE(opt); opt = DHCP_OPT_NEXT(opt); } if (is_ack && lease_time) { zscope_dhcp_bind(scope, dhcph->chaddr, session_ip, lease_time); } }
void *runAudience(void* idPtr) { long id = (long) idPtr; int sleepDuration = 1000; int roundsCompleted = 0; long dancer = NO_DANCER; for (roundsCompleted = 0; roundsCompleted != nRounds || nRounds == 0; roundsCompleted++) { // Vegetate printf("Audience %ld: Beginning vegetation \n", id); sleepDuration = rand() % SEC2USEC(10); printf("Audience %ld: Sleeping for %.2lf seconds\n", id, USEC2SEC(sleepDuration)); usleep(sleepDuration); //usleep(SEC2USEC(20)); // Select Dancer // Lock prevents audience members adding themselves while others are being signalled by runDancer() dancer = randomDancer(); pthread_mutex_lock(&(watchMutexes[dancer])); toWatch[dancer]++; validRequest[dancer] = true; printf("Audience %ld: Selected to watch dancer: %ld\n", id, dancer); pthread_mutex_unlock(&(watchMutexes[dancer])); // Watch // Wait on semaphore until dancer starts dancing sem_wait(&toWatchSemaphores[dancer]); printf("Audience %ld: Now Watching dancer: %ld\n", id, dancer); // Observe leave // Wait on semaphore until dancer finishes dancing sem_wait(&nowWatchingSemaphore); //Indicate that the audience member has finished watching pthread_mutex_lock(&nAudienceWatchingMutex); nAudienceWatching--; pthread_mutex_unlock(&nAudienceWatchingMutex); } printf("Audience %ld: Finished %d rounds, dying now.\n", id, nRounds); return NULL; }
/* * Session handling function stub. Just dumps small HTML page. */ void handle_session(long srv_socket_index, st_netfd_t cli_nfd) { static char resp[] = "HTTP/1.0 200 OK\r\nContent-type: text/html\r\n" "Connection: close\r\n\r\n<H2>It worked!</H2>\n"; char buf[512]; int n = sizeof(resp) - 1; struct in_addr *from = st_netfd_getspecific(cli_nfd); if (st_read(cli_nfd, buf, sizeof(buf), SEC2USEC(REQUEST_TIMEOUT)) < 0) { err_sys_report(errfd, "WARN: can't read request from %s: st_read", inet_ntoa(*from)); return; } if (st_write(cli_nfd, resp, n, ST_UTIME_NO_TIMEOUT) != n) { err_sys_report(errfd, "WARN: can't write response to %s: st_write", inet_ntoa(*from)); return; } RQST_COUNT(srv_socket_index)++; }
/** * Update speed meter. * @param[in] speed * @param[in] count */ void spdm_update(struct speed_meter *speed, uint64_t count) { uint64_t curr_time = zclock(false); uint64_t last_update = atomic_load_explicit(&speed->last_update, memory_order_acquire); if (curr_time - last_update >= SEC2USEC(1)) { if (atomic_compare_exchange_strong_explicit(&speed->last_update, &last_update, curr_time, memory_order_release, memory_order_relaxed)) { size_t i = atomic_load_explicit(&speed->i, memory_order_acquire); uint64_t speed_aux = atomic_load_explicit(&speed->speed_aux, memory_order_acquire); atomic_store_explicit(&speed->backlog[i].speed, speed_aux, memory_order_release); atomic_fetch_sub_explicit(&speed->speed_aux, speed_aux, memory_order_release); atomic_store_explicit(&speed->backlog[i].timestamp, last_update, memory_order_release); i++; if (SPEED_METER_BACKLOG == i) { i = 0; } atomic_store_explicit(&speed->i, i, memory_order_release); } } atomic_fetch_add_explicit(&speed->speed_aux, count, memory_order_release); }
hoxResult hoxSocketAPI::read_line( st_netfd_t fd, std::string& outLine, const int timeout ) { const char* FNAME = "hoxSocketAPI::read_line"; hoxResult result = hoxRC_ERR; char c; ssize_t nread; st_utime_t timeout_usecs; outLine.clear(); timeout_usecs = SEC2USEC( timeout ); // convert seconds -> microseconds /* Read until one of the following conditions is met: * (1) One while line is read. * (2) Timeout occurs. * (3) An error occurs. */ for ( ;; ) { /* Read one character at a time */ nread = st_read(fd, &c, 1, timeout_usecs); /* CASE 1: Network connection is closed */ if ( nread == 0 ) { hoxLog(LOG_INFO, "%s: Network connection closed.", FNAME); result = hoxRC_OK; break; } /* CASE 2: Possible error */ if ( nread < 0 ) { if ( errno == EINTR ) // The current thread was interrupted { continue; } else if ( errno == ETIME ) // The timeout occurred and no data was read { hoxLog(LOG_INFO, "%s: Timeout [%d secs] occurred.", FNAME, timeout); result = hoxRC_TIMEOUT; break; } else { hoxLog(LOG_SYS_WARN, "%s: Socket error.", FNAME); result = hoxRC_ERR; break; } } /* CASE 3: Read data OK */ if ( c == '\n' ) { result = hoxRC_OK; break; // Success. } else { outLine += c; // Impose some limit. if ( outLine.size() >= hoxNETWORK_MAX_MSG_SIZE ) { hoxLog(LOG_WARN, "%s: Maximum message's size [%d] reached.", FNAME, hoxNETWORK_MAX_MSG_SIZE ); hoxLog(LOG_WARN, "%s: Partial read message (64 bytes) = [%s ...].", FNAME, outLine.substr( 0, 64 ).c_str() ); result = hoxRC_ERR; break; } } } return result; }
int hoxSocketAPI::read_until_all( const st_netfd_t fd, const std::string& sWanted, std::string& sOutput, const int timeout /* = -1 */ ) { const unsigned int nMax = 10 * 1024; // 10-K limit sOutput.clear(); char c; // The byte just received. const size_t requiredSeen = sWanted.size(); size_t currentSeen = 0; int iResult = 0; int nTotal = 0; // Total bytes received so far. const st_utime_t timeout_usecs = ( timeout == -1 ? ST_UTIME_NO_TIMEOUT : SEC2USEC( timeout ) ); hoxCHECK_MSG(sizeof(char) == 1, HOX_ERR_SOCKET_OTHER, "size of char != 1"); /* Receive data until seeing all characters in the "wanted" string * or until the peer closes the connection */ while ( currentSeen < requiredSeen ) { iResult = st_read( fd, &c, 1, timeout_usecs ); if ( iResult == 1 ) { sOutput += c; if ( c == sWanted[currentSeen] ) // seen the next char? { ++currentSeen; continue; } currentSeen = 0; // Reset "what we have seen". if ( sOutput.size() >= nMax ) // Impose some limit. { hoxLog(LOG_WARN, "%s: *WARN* Max message's size [%d] reached.", __FUNCTION__, nMax); return HOX_ERR_SOCKET_LIMIT; } } else if ( iResult == 0 ) // Connection closed? { return HOX_ERR_SOCKET_CLOSED; } else // Some other socket error? { if ( errno == EINTR ) // The current thread was interrupted { continue; } else if ( errno == ETIME ) // The timeout occurred and no data was read { hoxLog(LOG_DEBUG, "%s: Timeout [%d secs].", __FUNCTION__, timeout); return HOX_ERR_SOCKET_TIMEOUT; } else { hoxLog(LOG_SYS_WARN, "%s: st_read failed", __FUNCTION__); return HOX_ERR_SOCKET_OTHER; } } } /* Chop off 'want' string. */ if ( currentSeen == requiredSeen && requiredSeen > 0 ) { nTotal = sOutput.size(); sOutput = sOutput.substr(0, nTotal - requiredSeen); } return nTotal; // Return the number of bytes received. }
/** * Authenticate and set client info. * @param[in] sess Client session. * @return Zero on success (or one of *_RC). */ static int session_authenticate(struct zsession *sess) { int ret = OTHER_RC; VALUE_PAIR *request_attrs = NULL, *response_attrs = NULL, *attrs = NULL; char msg[8192]; // WARNING: libfreeradius-client has unsafe working with this buffer. rc_handle *rh = zinst()->radh; struct in_addr ip_addr; char ip_str[INET_ADDRSTRLEN]; struct zcrules rules; crules_init(&rules); ip_addr.s_addr = htonl(sess->ip); if (unlikely(NULL == inet_ntop(AF_INET, &ip_addr, ip_str, sizeof(ip_str)))) { goto end; } if (unlikely(NULL == rc_avpair_add(rh, &request_attrs, PW_USER_NAME, ip_str, -1, 0))) { goto end; } if (unlikely(NULL == rc_avpair_add(rh, &request_attrs, PW_USER_PASSWORD, "", -1, 0))) { goto end; } if (unlikely(NULL == rc_avpair_add(rh, &request_attrs, PW_NAS_IDENTIFIER, zcfg()->radius_nas_identifier, -1, 0))) { goto end; } if (unlikely(NULL == rc_avpair_add(rh, &request_attrs, PW_CALLING_STATION_ID, ip_str, -1, 0))) { goto end; } ret = rc_auth(rh, 0, request_attrs, &response_attrs, msg); if (OK_RC != ret) { ZERO_LOG(LOG_ERR, "Session authentication failed for %s (code:%d)", ip_str, ret); goto end; } attrs = response_attrs; while (likely(NULL != attrs)) { switch (attrs->attribute) { case PW_FILTER_ID: crules_parse(&rules, attrs->strvalue); break; case PW_SESSION_TIMEOUT: atomic_store_explicit(&sess->max_duration, SEC2USEC(attrs->lvalue), memory_order_release); break; case PW_ACCT_INTERIM_INTERVAL: atomic_store_explicit(&sess->acct_interval, SEC2USEC(attrs->lvalue), memory_order_release); break; } attrs = attrs->next; } if (likely(rules.have.user_id && rules.have.login)) { struct zclient *client = sess->client; client_db_find_or_set_id(zinst()->client_db, rules.user_id, &client); if (client != sess->client) { // found pthread_rwlock_wrlock(&sess->lock_client); atomic_fetch_add_explicit(&client->refcnt, 1, memory_order_relaxed); client_release(sess->client); sess->client = client; client_session_add(sess->client, sess); pthread_rwlock_unlock(&sess->lock_client); } else { client_apply_rules(sess->client, &rules); } atomic_fetch_sub_explicit(&zinst()->unauth_sessions_cnt, 1, memory_order_release); // log successful authentication { UT_string rules_str; utstring_init(&rules_str); utstring_reserve(&rules_str, 1024); attrs = response_attrs; while (likely(NULL != attrs)) { switch (attrs->attribute) { case PW_FILTER_ID: utstring_printf(&rules_str, " %s", attrs->strvalue); break; default: break; } attrs = attrs->next; } zero_syslog(LOG_INFO, "Authenticated session %s (rules:%s)", ip_str, utstring_body(&rules_str)); utstring_done(&rules_str); } } else { ret = OTHER_RC; ZERO_LOG(LOG_ERR, "Session authentication failed for %s (code:%d)", ip_str, ret); } end: crules_free(&rules); if (request_attrs) rc_avpair_free(request_attrs); if (response_attrs) rc_avpair_free(response_attrs); return ret; }
/** * @param[in] section Config section. * @param[in,out] scope Config scope. */ int zconfig_scope_load(const config_setting_t *section, zconfig_scope_t *scope) { u_int uival = 0; const config_setting_t *option = NULL; memset(scope, 0, sizeof(*scope)); scope->name = strdup(section->name); option = config_setting_get_member(section, ZCFG_SCOPE_DEFAULT_CLIENT_RULES); if (0 != zcfg_load_client_rules(option, &scope->default_client_rules)) { ZLOG(LOG_ERR, "config:%s: failed to load %s option", scope->name, ZCFG_SCOPE_DEFAULT_CLIENT_RULES); return -1; } option = config_setting_get_member(section, ZCFG_SCOPE_RADIUS_AUTH); if (0 != zcfg_load_bool(option, &scope->radius.auth)) { ZLOG(LOG_ERR, "config:%s: failed to load %s option", scope->name, ZCFG_SCOPE_RADIUS_AUTH); return -1; } if (scope->radius.auth) { option = config_setting_get_member(section, ZCFG_SCOPE_RADIUS_ACCT); if (0 != zcfg_load_bool(option, &scope->radius.acct)) { ZLOG(LOG_ERR, "config:%s: failed to load %s option", scope->name, ZCFG_SCOPE_RADIUS_ACCT); return -1; } option = config_setting_get_member(section, ZCFG_SCOPE_RADIUS_CONFIG); if (0 != zcfg_load_string(option, &scope->radius.config)) { ZLOG(LOG_ERR, "config:%s: failed to load %s option", scope->name, ZCFG_SCOPE_RADIUS_CONFIG); return -1; } option = config_setting_get_member(section, ZCFG_SCOPE_RADIUS_NAS_ID); if (0 != zcfg_load_string(option, &scope->radius.nas_id)) { ZLOG(LOG_ERR, "config:%s: failed to load %s option", scope->name, ZCFG_SCOPE_RADIUS_NAS_ID); return -1; } } option = config_setting_get_member(section, ZCFG_SCOPE_SESSION_ACCT_INTERVAL); if (0 != zcfg_load_uint(option, &uival)) { ZLOG(LOG_ERR, "config:%s: failed to load %s option", scope->name, ZCFG_SCOPE_SESSION_ACCT_INTERVAL); return -1; } else { scope->session.acct_interval = SEC2USEC(uival); } option = config_setting_get_member(section, ZCFG_SCOPE_SESSION_AUTH_INTERVAL); if (0 != zcfg_load_uint(option, &uival)) { ZLOG(LOG_ERR, "config:%s: failed to load %s option", scope->name, ZCFG_SCOPE_SESSION_AUTH_INTERVAL); return -1; } else { scope->session.auth_interval = SEC2USEC(uival); } option = config_setting_get_member(section, ZCFG_SCOPE_SESSION_TIMEOUT); if (0 != zcfg_load_uint(option, &uival)) { ZLOG(LOG_ERR, "config:%s: failed to load %s option", scope->name, ZCFG_SCOPE_SESSION_TIMEOUT); return -1; } else { scope->session.timeout = SEC2USEC(uival); } option = config_setting_get_member(section, ZCFG_SCOPE_SESSION_IDLE_TIMEOUT); if (0 != zcfg_load_uint(option, &uival)) { ZLOG(LOG_ERR, "config:%s: failed to load %s option", scope->name, ZCFG_SCOPE_SESSION_IDLE_TIMEOUT); return -1; } else { scope->session.idle_timeout = SEC2USEC(uival); } option = config_setting_get_member(section, ZCFG_SCOPE_SUBNETS_CLIENT); if (0 != zcfg_load_subnet_list(option, &scope->client_subnets)) { ZLOG(LOG_ERR, "config:%s: failed to load %s option", scope->name, ZCFG_SCOPE_SUBNETS_CLIENT); return -1; } option = config_setting_get_member(section, ZCFG_SCOPE_SUBNETS_LOCAL); if (0 != zcfg_load_subnet_list(option, &scope->local_subnets)) { ZLOG(LOG_ERR, "config:%s: failed to load %s option", scope->name, ZCFG_SCOPE_SUBNETS_LOCAL); return -1; } option = config_setting_get_member(section, ZCFG_SCOPE_SUBNETS_LOCAL_EXCLUDE); if (0 != zcfg_load_subnet_list(option, &scope->local_subnets_exclusions)) { ZLOG(LOG_ERR, "config:%s: failed to load %s option", scope->name, ZCFG_SCOPE_SUBNETS_LOCAL_EXCLUDE); return -1; } option = config_setting_get_member(section, ZCFG_SCOPE_PORTS_WHITELIST); if (0 != zcfg_load_uint16_array(option, &scope->ports_whitelist)) { ZLOG(LOG_ERR, "config:%s: failed to load %s option", scope->name, ZCFG_SCOPE_PORTS_WHITELIST); return -1; } option = config_setting_get_member(section, ZCFG_SCOPE_DHCP_SNOOPING); if (0 != zcfg_load_bool(option, &scope->security.dhcp_snooping)) { ZLOG(LOG_ERR, "config:%s: failed to load %s option", scope->name, ZCFG_SCOPE_DHCP_SNOOPING); return -1; } option = config_setting_get_member(section, ZCFG_SCOPE_DHCP_DEFAULT_LEASE_TIME); if (0 != zcfg_load_uint(option, &uival)) { ZLOG(LOG_ERR, "config:%s: failed to load %s option", scope->name, ZCFG_SCOPE_DHCP_DEFAULT_LEASE_TIME); return -1; } else { scope->security.dhcp_default_lease_time = SEC2USEC(uival); } if (scope->security.dhcp_snooping) { option = config_setting_get_member(section, ZCFG_SCOPE_DYNAMIC_ARP_PROTECTION); if (0 != zcfg_load_bool(option, &scope->security.arp_protect)) { ZLOG(LOG_ERR, "config:%s: failed to load %s option", scope->name, ZCFG_SCOPE_DYNAMIC_ARP_PROTECTION); return -1; } option = config_setting_get_member(section, ZCFG_SCOPE_IP_VERIFY_SOURCE); if (0 != zcfg_load_bool(option, &scope->security.ip_protect)) { ZLOG(LOG_ERR, "config:%s: failed to load %s option", scope->name, ZCFG_SCOPE_IP_VERIFY_SOURCE); return -1; } } option = config_setting_get_member(section, ZCFG_SCOPE_BLACKLIST_ENABLED); if (0 != zcfg_load_bool(option, &scope->blacklist.enabled)) { ZLOG(LOG_ERR, "config:%s: failed to load %s option", scope->name, ZCFG_SCOPE_BLACKLIST_ENABLED); return -1; } if (scope->blacklist.enabled) { option = config_setting_get_member(section, ZCFG_SCOPE_BLACKLIST_FILE); if (0 != zcfg_load_string(option, &scope->blacklist.file)) { ZLOG(LOG_ERR, "config:%s: failed to load %s option", scope->name, ZCFG_SCOPE_BLACKLIST_FILE); return -1; } option = config_setting_get_member(section, ZCFG_SCOPE_BLACKLIST_RELOAD_INTERVAL); if (0 != zcfg_load_uint(option, &uival)) { ZLOG(LOG_ERR, "config:%s: failed to load %s option", scope->name, ZCFG_SCOPE_BLACKLIST_RELOAD_INTERVAL); return -1; } else { scope->blacklist.reload_interval = SEC2USEC(uival); } } return 0; }
void *runDancer(void *idPtr) { long id = (long)idPtr; unsigned int nWatchingMe = 0; bool validRequestsExist = false; while (1) { //Lock to prevent > 2 dancers being selected pthread_mutex_lock(&selectDancerMutex); if (dancerProOrAgedDone == 0 && (dancerAged == NO_DANCER || dancerProOrAged == NO_DANCER)) { //Check if there are any valid requests for dancers validRequestsExist = false; int i; for (i = 0; i != nDancers; i++) { validRequestsExist = validRequestsExist || validRequest[i]; } //This dancer can be selected if there are currently no requests or if this dancer has been requested if (!validRequestsExist || validRequest[id]) { if (id != previousAged && id != previousProOrAged) { if (id < nAgedDancers && dancerAged == NO_DANCER) { printf("%ld became new aged dancer\n", id); dancerAged = id; } else if (dancerProOrAged == NO_DANCER) { //Handle special case where there are 2 aged dancers only. Prevents both dancing at same time if (id >= nAgedDancers || (nAgedDancers > 2)) { printf("%ld became new pro dancer\n", id); dancerProOrAged = id; } } } //Regardless of whether or not the request is fulfilled, the request is no longer valid validRequest[id] = false; } } pthread_mutex_unlock(&selectDancerMutex); if (dancerAged == id || dancerProOrAged == id) { //Wait until the other dancer has also been selected while (dancerAged == NO_DANCER || dancerProOrAged == NO_DANCER) { }; assert(dancerAged != dancerProOrAged); printf("%ld now dancing on stage\n", id); // Lock prevents audience members adding themselves while others are being signalled by runDancer() printf("Signalling %d audience members waiting for dancer %ld\n", toWatch[id], id); pthread_mutex_lock(&(watchMutexes[id])); nWatchingMe = toWatch[id]; for (; toWatch[id] != 0; toWatch[id]--) { sem_post(&toWatchSemaphores[id]); pthread_mutex_lock(&nAudienceWatchingMutex); nAudienceWatching++; pthread_mutex_unlock(&nAudienceWatchingMutex); } pthread_mutex_unlock(&(watchMutexes[id])); if (dancerAged == id) { agedDancing = true; } else if (dancerProOrAged == id) { proOrAgedDancing = true; } /* Dance - ensure both dancers are dancing simultaneously */ while (!agedDancing || !proOrAgedDancing) { } assert(agedDancing == true); assert(proOrAgedDancing == true); assert(dancerAged != NO_DANCER); assert(dancerProOrAged != NO_DANCER); assert(dancerAged != previousAged); assert(dancerAged != previousProOrAged); assert(dancerProOrAged != previousAged); assert(dancerProOrAged != previousProOrAged); assert(dancerAged != dancerProOrAged); assert(dancerAged < nAgedDancers); //Dance usleep(SEC2USEC(10)); // Leave stage printf("%ld finished dancing on stage\n", id); for (; nWatchingMe != 0; nWatchingMe--) { sem_post(&nowWatchingSemaphore); } while (nAudienceWatching != 0) {} assert(nAudienceWatching == 0); //Let the aged dancer thread handle the resetting of dancers if (id == dancerAged) { //Lock prevents new dancers being selected while dancers are being reset pthread_mutex_lock(&selectDancerMutex); while (!dancerProOrAgedDone) {} //Reset nValidRequests as previously invalid requests may now be valid int i; for (i = 0; i != nDancers; i++) { validRequest[i] = false; } for (i = 0; i != nDancers; i++) { if (toWatch[i] > 0) {validRequest[i] = true;} } //Reset dancers previousProOrAged = dancerProOrAged; previousAged = dancerAged; dancerAged = NO_DANCER; dancerProOrAged = NO_DANCER; agedDancing = false; proOrAgedDancing = false; //Allow the pro dancer to leave sem_post(&leaveTogetherSemaphore); pthread_mutex_unlock(&selectDancerMutex); } else { dancerProOrAgedDone = true; //Semaphore prevents pro dancer continuing before aged dancer has reset dancers sem_wait(&leaveTogetherSemaphore); dancerProOrAgedDone = false; } printf("%ld has left stage\n", id); } } return NULL; }
void *handle_connection(void *arg) { st_netfd_t client_nfd = (st_netfd_t)arg; struct http_stream *s = http_stream_create(HTTP_SERVER, SEC2USEC(5)); char buf[4*1024]; int error = 0; struct http_stream *cs = NULL; uri_t *u = uri_new(); int should_close = 1; for (;;) { should_close = 1; if (s->status != HTTP_STREAM_OK) break; cs = NULL; error = 0; s->timeout = SEC2USEC(5); int status = http_stream_request_read(s, client_nfd); s->timeout = SEC2USEC(30); // longer timeout for the rest if (status != HTTP_STREAM_OK) { if (s->status == HTTP_STREAM_CLOSED || s->status == HTTP_STREAM_TIMEOUT) { error = 1; } else { error = 400; } goto release; } cs = http_stream_create(HTTP_CLIENT, SEC2USEC(30)); //http_request_debug_print(s->req); fprintf(stderr, "request uri: %s\n", s->req->uri); const char *error_at = NULL; uri_clear(u); if (uri_parse(u, s->req->uri, strlen(s->req->uri), &error_at) == 0) { fprintf(stderr, "uri_parse error: %s\n", error_at); error = 400; goto release; } uri_normalize(u); if (http_stream_connect(cs, u->host, u->port) != HTTP_STREAM_OK) { error = 504; goto release; } http_request_header_remove(s->req, "Accept-Encoding"); http_request_header_remove(s->req, "Proxy-Connection"); /* TODO: need to expose a copy api for http message */ http_request_t *tmp_req = cs->req; cs->req = s->req; char *request_uri = uri_compose_partial(u); char *tmp_uri = s->req->uri; cs->req->uri = request_uri; if (http_stream_request_send(cs) != HTTP_STREAM_OK) { error = 504; goto release; } cs->req = tmp_req; s->req->uri = tmp_uri; free(request_uri); /* TODO: fix this. post might not contain data. probably move this logic into stream */ size_t total = 0; if (g_strcmp0("POST", s->req->method) == 0) { for (;;) { ssize_t nr = sizeof(buf); status = http_stream_read(s, buf, &nr); fprintf(stderr, "server http_stream_read nr: %zd\n", nr); if (nr < 0 || status != HTTP_STREAM_OK) { error = 1; goto release; } if (nr == 0) break; /*fwrite(buf, sizeof(char), nr, stdout);*/ ssize_t nw = st_write(cs->nfd, buf, nr, s->timeout); if (nw != nr) { error=1; goto release; } fprintf(stderr, "st_write nw: %zd\n", nr); total += nr; } fprintf(stderr, "http_stream_read total: %zu\n", total); } if (http_stream_response_read(cs) != HTTP_STREAM_OK) { error = 502; goto release; } /* TODO: properly create a new response and copy headers */ http_response_t *tmp_resp = s->resp; s->resp = cs->resp; s->resp->http_version = "HTTP/1.1"; http_response_header_remove(s->resp, "Content-Length"); http_response_header_remove(s->resp, "Transfer-Encoding"); if (s->resp->status_code != 204) http_response_header_append(s->resp, "Transfer-Encoding", "chunked"); ssize_t nw = http_stream_response_send(s, 0); s->resp = tmp_resp; fprintf(stderr, "http_stream_response_send: %zd\n", nw); if (s->resp->status_code != 204 && (cs->content_size > 0 || cs->transfer_encoding == TE_CHUNKED)) { total = 0; fprintf(stderr, "content size: %zd\n", cs->content_size); for (;;) { ssize_t nr = sizeof(buf); status = http_stream_read(cs, buf, &nr); fprintf(stderr, "client http_stream_read nr: %zd\n", nr); if (nr <= 0 || status != HTTP_STREAM_OK) break; /*fwrite(buf, sizeof(char), nr, stdout);*/ total += nr; if (http_stream_send_chunk(s, buf, nr) != HTTP_STREAM_OK) break; } fprintf(stderr, "written to client: %zu\n", total); if (total > 0 && s->status == HTTP_STREAM_OK) { http_stream_send_chunk_end(s); } else { fprintf(stderr, "for request: %s status: %d\n", s->req->uri, s->status); } } release: if (!error) { if ((g_strcmp0("HTTP/1.1", s->req->http_version) == 0) && (g_strcmp0(http_request_header_getstr(s->req, "Connection"), "close") != 0)) { // if HTTP/1.1 client and no Connection: close, then don't close should_close = 0; } else if (g_strcmp0(http_request_header_getstr(s->req, "Connection"), "keepalive") == 0) { should_close = 0; } } http_request_clear(s->req); uri_clear(u); if (cs) http_stream_close(cs); /* TODO: break loop if HTTP/1.0 and not keep-alive */ if (error) { fprintf(stderr, "ERROR: %d STATUS: %d, exiting\n", error, s->status); /* TODO: use reason string */ if (error >= 400 && s->status != HTTP_STREAM_CLOSED) { http_response_free(s->resp); s->resp = http_response_new(error, "Error"); http_response_header_append(s->resp, "Content-Length", "0"); s->status = HTTP_STREAM_OK; /* TODO: might want to move this logic into http_stream */ http_stream_response_send(s, 0); } break; } if (should_close) break; } fprintf(stderr, "exiting handle_connection (should_close: %u)\n", should_close); uri_free(u); http_stream_close(s); return NULL; }