void process_rstheader(rio_t *rp) { char buf[MAXLEN]; rio_readlineb(rp, buf, MAXLEN); while(strcmp(buf, "\r\n")) rio_readlineb(rp, buf, MAXLEN); }
void echo(int connfd){ size_t n; char buf[MAXLINE]; rio_t rio; rio_readinitb(&rio, connfd); if((n = rio_readlineb(&rio, buf, MAXLINE)) < 0){ fprintf(stderr, "rio_readlineb error \n"); exit(0); } while(n != 0){ printf("server received %d bytes\n", n); if(rio_writen(connfd, buf, n) != n){ fprintf(stderr, "rio_writen error\n"); exit(0); } if((n = rio_readlineb(&rio, buf, MAXLINE)) < 0){ fprintf(stderr, "rio_readlineb error \n"); exit(0); } } }
/* $begin read_requesthdrs */ void send_requesthdrs(rio_t *rp, int file, char* host) { char buf[MAXLINE]; int hosthdr = 0; int bufLen; bufLen = rio_readlineb(rp, buf, MAXLINE); rio_writen(file, buf, bufLen); while(strcmp(buf, "\r\n")) { bufLen = rio_readlineb(rp, buf, MAXLINE); if(!strncasecmp(buf, "Connection", strlen("Connection"))) { continue; } if(!strncasecmp(buf, "Proxy-Connection", strlen("Proxy-Connection"))) { continue; } if(!strncasecmp(buf, "User-Agent", strlen("User-Agent"))) { continue; } if(!strncasecmp(buf, "Accept", strlen("Accept"))) { continue; } if(!strncasecmp(buf, "Accept-Encoding", strlen("Accept-Encoding"))) { continue; } if(!strncasecmp(buf, "Host", strlen("Host"))) { hosthdr = 1; } //this is broken. When we understand how to do Host, we'll fix it. if(0 && !strncasecmp(buf,"\r\n",strlen("\r\n")) && !hosthdr) { char tmp[MAXLINE]; sprintf(tmp, "Host: %s\r\n",host); rio_writen(file, tmp, strlen(tmp)); printf(tmp); } rio_writen(file, buf, bufLen); if(!strncasecmp(buf,"Cookie",strlen("Cookie"))) { printf("Cookie: <POTENTIALLY BINARY DATA REDACTED>\r\n"); } else { printf(buf); } } }
/* Send Request from browser to the server*/ int SendRequest(int clientfd, rio_t serverrio_t, char* hostname, char* uriname){ int i,byte; int flag[5] = {0}; char headerBuff[MAXHEADERSIZE]; char requestLine[MAXHEADERSIZE]; sprintf(requestLine,"GET %s HTTP/1.0\r\n",uriname); rio_writen(clientfd,requestLine,strlen(requestLine)); byte = rio_readlineb(&serverrio_t,headerBuff,sizeof(headerBuff)); if( strstr(headerBuff,"Host:") != NULL){ getHostname(headerBuff,hostname); } sprintf(requestLine,"Host: %s\r\n",hostname); rio_writen(clientfd,requestLine,strlen(requestLine)); while( (byte = rio_readlineb(&serverrio_t,headerBuff,sizeof(headerBuff))) >0){ if(strncmp(headerBuff,"GET",3) == 0){ } else if(strncmp(headerBuff,"Host",4) == 0){ } else if(strncmp(headerBuff,"\r",1) == 0){ break; } else if(strncmp(headerBuff,"User-Agent:",11) == 0){ rio_writen(clientfd,headerSet[0],strlen(headerSet[0])); flag[0] = 1; } else if(strncmp(headerBuff,"Accept:",7) == 0){ rio_writen(clientfd,headerSet[1],strlen(headerSet[1])); flag[1] = 1; } else if(strncmp(headerBuff,"Accept-Encoding:",16) == 0){ rio_writen(clientfd,headerSet[2],strlen(headerSet[2])); flag[2] = 1; } else if(strncmp(headerBuff,"Connection:",11) == 0){ rio_writen(clientfd,headerSet[3],strlen(headerSet[3])); flag[3] = 1; } else if(strncmp(headerBuff,"Proxy-Connection:",17) == 0){ rio_writen(clientfd,headerSet[4],strlen(headerSet[4])); flag[4] = 1; } else{ rio_writen(clientfd,headerBuff,strlen(headerBuff)); } } for(i = 0 ; i < 5 ;i++){ if(flag[i] == 0){ rio_writen(clientfd,headerSet[i],strlen(headerSet[i])); flag[i] = 1; } } rio_writen(clientfd,"\r\n",strlen("\r\n")); return 1; }
void read_requesthdrs(rio_t *rp) { char buf[MAXLINE]; rio_readlineb(rp, buf, MAXLINE); while(strcmp(buf, "\r\n")) { rio_readlineb(rp, buf, MAXLINE); } }
void read_requesthdrs(rio_t *rp) { char buf[MAXLINE]; rio_readlineb(rp, buf, MAXLINE); while (strcmp(buf, "\r\n")) { rio_readlineb(rp, buf, MAXLINE); printf("%s", buf); } return; }
/* parse_request parses request from client and stores the * information in a client_request structure */ int parse_request(struct client_request *request, int clientfd) { char buf[MAXBUF], method[MAXLINE], uri[MAXLINE], port[MAXLINE], *ptr; rio_t rio; port[0] = 0; rio_readinitb(&rio, clientfd); rio_readlineb(&rio, buf, MAXLINE - 1); if (sscanf(buf, "%s %s %*s", method, uri) < 2) { printf("parsing error %s\n", buf); return -1; } strcpy(request->request_line, buf); if (sscanf(uri, "http://%[^:/]:%[^/]/%*s", request->host, port) < 1) return -1; if (*port == 0) request->server_port = 80; else request->server_port = atoi(port); if (strcmp(method, "GET")) return -2; sprintf(request->request, "GET %s HTTP/1.0\r\nHost: %s\r\n", uri, request->host); /* reads all headers */ while (1) { rio_readlineb(&rio, buf, MAXLINE - 1); /* need to change connection header to close */ if ((ptr = strstr(buf, "Connection:")) != NULL) { strcat(request->request, "Connection: close\n"); continue; } /* need to delete keep-alive header */ if ((ptr = strcasestr(buf, "keep-alive")) != NULL) continue; /* host is already in the header */ if ((ptr = strstr(buf, "Host:")) != NULL) continue; strcat(request->request, buf); if (*buf == '\r' && *(buf + 1) == '\n') break; } request->request_length = strlen(request->request); return 0; }
/* $begin hdr_concat */ void hdr_concat(rio_t *rio_client,char *hdr_server,char *hostname, char *pathname) { char add_info[MAXLINE]; char buf[MAXLINE]; char *ptr; int n=0; int off=0; if (rio_readlineb(rio_client,buf,MAXLINE)!=0){ ptr=buf; if (!strncmp(buf,"Host:",5)){ ptr+=5; *ptr='\0'; strstr(hostname,ptr); } } memset(buf,0,MAXLINE); memset(add_info,0,MAXLINE); while((n=rio_readlineb(rio_client,buf,MAXLINE))>0){ if (!strcmp(buf,"\r\n")) break; ptr=buf; if (!strncmp(buf,"User-Agent:",11)|| !strncmp(buf,"Accept:",7)|| !strncmp(buf,"Accept-Encoding:",16)|| !strncmp(buf,"Connection:",11)|| !strncmp(buf,"Proxy-Connection:",17)){ continue; } else{ memcpy(add_info+off,buf,n); memset(buf,0,MAXLINE); off+=n; } } sprintf(hdr_server,"GET %s HTTP/1.0\r\n",pathname); sprintf(hdr_server,"%sHost: %s\r\n",hdr_server,hostname); strcat(hdr_server,user_agent_hdr); strcat(hdr_server,accept_hdr); strcat(hdr_server,accept_encoding_hdr); strcat(hdr_server,conn); strcat(hdr_server,proxy_conn); strcat(hdr_server,"\r\n"); strcat(hdr_server,add_info); }
void check_clients(Pool *p) { int i, connfd, n; char buf[MAXLINE]; rio_t rio; for(i = 0; (i <= p->maxi) && (p->nready > 0); i++){ connfd = p->clientfd[i]; rio = p->clientrio[i]; if((connfd > 0) && (FD_ISSET(connfd, &p->ready_set))){ p->nready --; if((n = rio_readlineb(&rio, buf, MAXLINE)) != 0 ){ byte_cnt += n; printf("Server received %d (%d total) bytes on fd %d\n", n, byte_cnt, connfd); rio_writen(connfd, buf, n); } else { close(connfd); FD_CLR(connfd, &p->read_set); p->clientfd[i] = -1; } } } }
int send_request(rio_t *rio, char *buf, struct status_line *status, int serverfd, int clientfd) { int len; if (strcmp(status->method, "CONNECT")) { len = snprintf(buf, MAXLINE, "%s %s %s\r\n" \ "Connection: close\r\n", status->method, *status->path ? status->path : "/", status->version); if ((len = rio_writen(serverfd, buf, len)) < 0) return len; while (len != 2) { if ((len = rio_readlineb(rio, buf, MAXLINE)) < 0) return len; if (memcmp(buf, "Proxy-Connection: ", 18) == 0 || memcmp(buf, "Connection: ", 12) == 0) continue; if ((len = rio_writen(serverfd, buf, len)) < 0) return len; } if (rio->rio_cnt && (len = rio_writen(serverfd, rio->rio_bufptr, rio->rio_cnt)) < 0) return len; return 20; } else { len = snprintf(buf, MAXLINE, "%s 200 OK\r\n\r\n", status->version); if ((len = rio_writen(clientfd, buf, len)) < 0) return len; return 300; } }
ssize_t Rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen) { ssize_t rc; if ((rc = rio_readlineb(rp, usrbuf, maxlen)) < 0) unix_error("Rio_readlineb error"); return rc; }
/****************************************************************************** * subroutine: parse_requestline * * purpose: parse the content of request line * * parameters: id - the index of the client in the pool * * p - a pointer of the pool data structure * * context - a pointer refers to HTTP context * * is_closed - an indicator if the current transaction is closed * * return: 0 on success -1 on error * ******************************************************************************/ int parse_requestline(int id, pool *p, HTTPContext *context, int *is_closed) { char buf[MAX_LINE]; memset(buf, 0, MAX_LINE); if (rio_readlineb(&p->clientrio[id], buf, MAX_LINE) < 0) { *is_closed = 1; Log("Error: rio_readlineb error in process_request \n"); serve_error(p->clientfd[id], "500", "Internal Server Error", "The server encountered an unexpected condition.", *is_closed); return -1; } if (sscanf(buf, "%s %s %s", context->method, context->uri, context->version) < 3) { *is_closed = 1; Log("Info: Invalid request line: '%s' \n", buf); serve_error(p->clientfd[id], "400", "Bad Request", "The request is not understood by the server", *is_closed); return -1; } Log("Request: method=%s, uri=%s, version=%s \n", context->method, context->uri, context->version); return 0; }
void doit(int fd) { int clientfd, port, size = 0; ssize_t linecounter; rio_t client_rio, server_rio; char buf[MAXLINE], method[MAXLINE], uri[MAXLINE], version[MAXLINE], serveraddr[MAXLINE], path[MAXLINE], message[MAXLINE]; //read the HTTP request rio_readinitb(&client_rio, fd); rio_readlineb(&client_rio, buf, MAXLINE); sscanf(buf, "%s %s %s", method, uri, version); if(strcasecmp(method, "GET") != 0) { clienterror(fd, method, "501", "Not Implemented", "Proxy does not implement this method"); return; } read_requesthdrs(&client_rio); //parse it to determine the name of the end server port = parse_uri(uri, serveraddr, path); //filter if(is_blocked_address(serveraddr)) { clienterror(fd, serveraddr, "403", "Forbidden", "Proxy does not access this server"); return; } //open a connection to the end server if((clientfd = open_clientfd(serveraddr, port)) < 0) { clienterror(fd, serveraddr, "404", "Not Found", "Proxy does not found this server"); return; } //send it the request sprintf(message, "GET %s HTTP/1.0\r\n", path); rio_writen(clientfd, message, strlen(message)); sprintf(message, "HOST: %s\r\n\r\n", serveraddr); rio_writen(clientfd, message, strlen(message)); //receive the reply, and forward the reply to the browser if the request is not blocked. rio_readinitb(&server_rio, clientfd); while((linecounter = rio_readlineb(&server_rio, message, MAXLINE)) != 0) { rio_writen(fd, message, linecounter); size += linecounter; } //log sem_wait(&mutex); log_report(serveraddr, size); sem_post(&mutex); }
ssize_t Rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen) { ssize_t rt; rt = rio_readlineb(rp, usrbuf, maxlen); if (rt == -1) unix_error("Rio_readlineb error"); return rt; }
ssize_t Rio_readlineb_s(rio_t *rp, void *usrbuf, size_t maxlen){ ssize_t rc; if ((rc = rio_readlineb(rp, usrbuf, maxlen)) < 0){ printf("Rio_readlineb error\n"); rc = 0; } return rc; }
/** @brief Reads all the request headers until CRLF. * @param rio The Robust IO buffer. * @return none. */ int read_request_headers(rio_t *rio) { char temp_buf[MAXLINE]; int rc; if ((rc = rio_readlineb(rio, temp_buf, MAXLINE)) < 0) { dbg_printf("rio_readlineb error\n"); return rc; } /* Keep reading header lines until CRLF */ while(strcmp(temp_buf, "\r\n") != 0) { if ((rc = rio_readlineb(rio, temp_buf, MAXLINE)) < 0 ) { dbg_printf("rio_readlineb error\n"); return rc; } } return SUCCESS; }
/****************************************************************************** * subroutine: parse_requestheaders * * purpose: parse the content of request headers * * parameters: id - the index of the client in the pool * * p - a pointer of the pool data structure * * context - a pointer refers to HTTP context * * is_closed - an indicator if the current transaction is closed * * return: 0 on success -1 on error * ******************************************************************************/ int parse_requestheaders(int id, pool *p, HTTPContext *context, int *is_closed) { int ret, cnt = 0, has_contentlen = 0, port; char buf[MAX_LINE], header[MIN_LINE], data[MIN_LINE], pbuf[MIN_LINE]; context->content_len = -1; do { if ((ret = rio_readlineb(&p->clientrio[id], buf, MAX_LINE)) < 0) break; cnt += ret; // if request header is larger than 8196, reject request if (cnt > MAX_LINE) { *is_closed = 1; serve_error(p->clientfd[id], "400", "Bad Request", "Request header too long.", *is_closed); return -1; } // parse Host header if (strstr(buf, "Host:")) { if (sscanf(buf, "%s: %s:%s", header, data, pbuf) > 0) { port = (int)strtol(pbuf, (char**)NULL, 10); if (port == STATE.s_port) { context->is_secure = 1; Log("Secure connection \n"); } } } if (strstr(buf, "Connection: close")) *is_closed = 1; if (strstr(buf, "Content-Length")) { has_contentlen = 1; if (sscanf(buf, "%s %s", header, data) > 0) context->content_len = (int)strtol(data, (char**)NULL, 10); Log("Debug: content-length=%d \n", context->content_len); } } while(strcmp(buf, "\r\n")); if ((!has_contentlen) && (!strcasecmp(context->method, "POST"))) { serve_error(p->clientfd[id], "411", "Length Required", "Content-Length is required.", *is_closed); return -1; } return 0; }
ssize_t Rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen) { ssize_t rc; if ((rc = rio_readlineb(rp, usrbuf, maxlen)) < 0) { if (errno != ECONNRESET) unix_error("Rio_readlineb error"); } return rc; }
void echo(int connfd){ size_t n; char buf[MAXLINE]; rio_t rio; rio_readinitb(&rio,connfd); while((n=rio_readlineb(&rio,buf,MAXLINE))!=0){ printf("server received %d bytes\n",n); rio_writen(connfd,buf,n); } }
void read_msg(int fd, char *recvbuf) { rio_t rp; int ret = 0; rio_readinitb(&rp,fd); int length = 1; length = rio_readlineb(&rp,recvbuf,MAX_MSG_LEN); if(length<=0) printf("end!!!"); return; }
ssize_t Rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen) { ssize_t rc; int max=3; while ((rc = rio_readlineb(rp, usrbuf, maxlen)) < 0){ sleep(1); max--; if (max==0){ return -1; } } return rc; }
/** @brief Handles HTTP GET requests. * Reads and parses the request, the uri, and dispatches the appropriate * functions to serve content. * @param conn_fd The connection file descriptor. * @return none. */ void handle_request(int conn_fd) { rio_t rio; char request[MAXLINE], uri[MAXLINE]; char file_name[MAXLINE], cgi_args[MAXLINE]; int is_static; struct stat st_buf; Rio_readinitb(&rio, conn_fd); /* Read first header line of incoming request. */ if (rio_readlineb(&rio, request, MAXLINE) < 0) { dbg_printf("rio_readlineb error\n"); return; } dbg_printf("Request: %s", request); if (parse_request_header(conn_fd, request, uri) != SUCCESS) { return; } if (read_request_headers(&rio) != SUCCESS) { return; } find_file(uri, file_name, cgi_args, &is_static); if (stat(file_name, &st_buf) < 0) { dbg_printf("404: File not found: %s\n", file_name); send_error_msg(conn_fd, "404", "File not found"); return; } /* Handle static content */ if (is_static) { if (!(S_ISREG(st_buf.st_mode)) || !(S_IRUSR & st_buf.st_mode)) { dbg_printf("403: Can't read the file: %s\n", file_name); send_error_msg(conn_fd, "403", "Can't read the file."); return; } serve_static(conn_fd, file_name, st_buf.st_size); } /* Handle dynamic content */ else { if (!(S_ISREG(st_buf.st_mode)) || !(S_IXUSR & st_buf.st_mode)) { dbg_printf("403: Can't run the CGI program: %s\n", file_name); send_error_msg(conn_fd, "403", "Can't run the CGI program."); return; } serve_dynamic(conn_fd, file_name, cgi_args); } }
void echo(int fd) { size_t n; char buf[MAXLINE]; rio_t rio; rio_readinitb(&rio, fd); while((n = rio_readlineb(&rio, buf, MAXLINE)) != 0) { printf("server received %d bytes\n", n); memset(buf, 0, MAXLINE); snprintf(buf, sizeof(buf), "server roger that\n"); rio_writen(fd, buf, strlen(buf)); } }
void *proxy(void *vargp) { Pthread_detach(Pthread_self()); int serverfd; int clientfd = *(int *)vargp; free(vargp); rio_t rio; rio_readinitb(&rio, clientfd); struct status_line status; char buf[MAXLINE]; int flag; #ifdef PROXY_CACHE char objectbuf[MAX_OBJECT_SIZE]; #endif if ((flag = rio_readlineb(&rio, buf, MAXLINE)) > 0) { if (parseline(buf, &status) < 0) fprintf(stderr, "parseline error: '%s'\n", buf); #ifdef PROXY_CACHE else if (cacheable(&status) && (flag = cache_find(status.line, objectbuf))) { if (rio_writen(clientfd, objectbuf, flag) < 0) log(cache_write); } #endif else if ((serverfd = open_clientfd(status.hostname, status.port)) < 0) log(open_clientfd); else { if ((flag = send_request(&rio, buf, &status, serverfd, clientfd)) < 0) log(send_request); else if (interrelate(serverfd, clientfd, buf, flag #ifdef PROXY_CACHE ,objectbuf, &status #endif ) < 0) log(interrelate); close(serverfd); } } close(clientfd); return NULL; }
void doit(int fd) { int is_static; struct stat sbuf; char buf[MAXLINE],method[MAXLINE],uri[MAXLINE],version[MAXLINE]; char filename[MAXLINE],cgiargs[MAXLINE]; rio_t rio; rio_readinitb(&rio,fd); rio_readlineb(&rio,buf,MAXLINE); sscanf(buf,"%s %s %s",method,uri,version); if(strcasecmp(method,"GET")) { clienterror(fd,method,"501","Not Implemented", "Tiny does not implement this method"); return; } read_requesthdrs(&rio); is_static = parse_uri(uri,filename,cgiargs); if(stat(filename,&sbuf) < 0) { clienterror(fd,filename,"404","Not found", "Tiny couldn't find this file"); return; } if(is_static) { if(!(S_ISREG(sbuf.st_mode)) || !(S_IRUSR & sbuf.st_mode)) { clienterror(fd,filename,"403","Forbidden", "Tiny could'n read the file"); return; } serve_static(fd,filename,sbuf.st_size); }else { if(!(S_ISREG(sbuf.st_mode)) || !(S_IXUSR & sbuf.st_mode)) { clienterror(fd,filename,"403","Forbidden", "Tiny could'nt run the CGI program"); return; } serve_dynamic(fd,filename,cgiargs); } }
int read_blocked_address() { int fd, n, num_addr = 0; rio_t rio; char buf[MAXLINE]; if((fd = open(FILTER_FILE, O_RDONLY, 0)) < 0) { fprintf(stderr, "Proxy does not found filter file"); exit(1); } rio_readinitb(&rio, fd); while((n = rio_readlineb(&rio, buf, MAXLINE)) > 0) { strncpy(blocked_address[num_addr], buf, n - 1); /* 忽略结尾的换行符 */ num_addr++; } close(fd); return num_addr; }
void process(int fd, int efd, char *rstline, char *method, char *uri, char *protocol, char *filename, char *filetype, char *response) { rio_t rio; int err; struct stat sbuf; char *file; rio_readinitb(&rio, fd); if((err = rio_readlineb(&rio, rstline, MAXLEN)) <= 0) { if((err = epoll_ctl(efd, EPOLL_CTL_DEL, fd, NULL)) < 0) FATAL("PROCESS.EPOLL_CTL"); close(fd); } else { sscanf(rstline, "%s %s %s", method, uri, protocol); if(strcasecmp(method, "GET")) //TEST ERR(fd, efd); if(uri[0] != '/') ERR(fd, efd); process_rstheader(&rio); parse_uri(uri, filename, filetype); if(get_fileinfo(filename) < 0) call_403(fd); else { lstat(filename, &sbuf); err = open(filename, O_RDONLY, 0); file = mmap(0 ,sbuf.st_size, PROT_READ, MAP_PRIVATE, err, 0); close(err); sprintf(response, "HTTP/1.1 200 OK\r\n"); sprintf(response, "%sContent-length: %lu\r\n", response, sbuf.st_size); sprintf(response, "%sContent-type: %s\r\n\r\n", response, filetype); rio_writen(fd, response, strlen(response)); rio_writen(fd, file, sbuf.st_size); munmap(file, sbuf.st_size); } } }
void parse_request_header(int client_fd, Request *request) { size_t n; rio_t client_rio; char buffer[MAXLINE]; rio_readinitb(&client_rio, client_fd); copy_single_line_str(&client_rio, request->request_str); copy_single_line_str(&client_rio, request->host_str); #ifdef DEBUG printf("request str:%s", request->request_str); printf("host str: %s", request->host_str); #endif while ((n = rio_readlineb(&client_rio, buffer, MAXLINE)) != 0) { #ifdef DEBUG printf("%s", buffer); #endif if (!strcmp(buffer, "\r\n")) { break; } } }
void home_page(char *host, char *fname) { int fd, n; char line[MAXLINE]; rio_t rio; fd = tcpConnect(host, SERV); /* blocking connect() */ n = snprintf(line, sizeof(line), GET_CMD, fname,host); rio_writen(fd, line, n); rio_readinitb(&rio, fd); while ((n = rio_readlineb(&rio, line, sizeof(line))) > 1) { if (strcmp(line,END_OF_HTML) == 0) { break; } parseUrl(line,host); } printf("end-of-file on home page\n"); close(fd); }
void *do_get_read(void *vptr) { int fd, n; char line[MAXLINE]; struct file *fptr; rio_t rio; fptr = (struct file *) vptr; fd = tcpConnect(fptr->f_host, SERV); fptr->f_fd = fd; printf("do_get_read for name:%s host:%s, fd %d, thread %d\n", fptr->f_name,fptr->f_host, fd, fptr->f_tid); write_get_cmd(fptr); /* write() the GET command */ rio_readinitb(&rio,fd); while((n = rio_readlineb(&rio,line,sizeof(line))) > 1) { printf("read %d bytes from name:%s host:%s\n", n, fptr->f_name,fptr->f_host); if (strcmp(line, END_OF_HTML) == 0) { break; } parseUrl(line,fptr->f_host); } printf("end-of-file on name:%s host:%s\n", fptr->f_name,fptr->f_host); close(fd); fptr->f_flags = F_DONE; /* clears F_READING */ pthread_mutex_lock(&ndone_mutex); ndone++; pthread_cond_signal(&ndone_cond); pthread_mutex_unlock(&ndone_mutex); return(fptr); /* terminate thread */ }