diff --git a/.vscode/settings.json b/.vscode/settings.json index aa9a253..b43fea8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,6 +5,7 @@ "files.associations": { "socket_wrapper.h": "c", "stdalign.h": "c", - "timerhelper.h": "c" + "timerhelper.h": "c", + "termio.h": "c" } } \ No newline at end of file diff --git a/Makefile b/Makefile index 5a0fe7d..fe4e98f 100644 --- a/Makefile +++ b/Makefile @@ -9,11 +9,13 @@ all: socket_wrapper.o: socket_wrapper.c socket_wrapper.h $(CC) -c socket_wrapper.c -o socket_wrapper.o $(CFLAGS) +display_bar.o: display_bar.c display_bar.h + $(CC) -c display_bar.c -o display_bar.o $(CFLAGS) client: socket_wrapper.o client.c $(CC) -o client client.c socket_wrapper.o $(CFLAGS) -server: socket_wrapper.o server.c - $(CC) -o server server.c socket_wrapper.o $(CFLAGS) +server: socket_wrapper.o server.c display_bar.o + $(CC) -o server server.c socket_wrapper.o display_bar.o $(CFLAGS) p-server: socket_wrapper.o p-server.c $(CC) -o p-server p-server.c socket_wrapper.o $(CFLAGS) p-client: socket_wrapper.o p-client.c diff --git a/client.c b/client.c index 69e8307..993c0f0 100644 --- a/client.c +++ b/client.c @@ -238,7 +238,7 @@ static bool init_queue_from_chararray(simple_queue_t * restrict q,const char ** // q have lifetime of arg f. // do not close file f before queue closed. static bool init_queue_from_file(simple_queue_t * restrict q, FILE * f){ - q->method = From_CharArray; + q->method = From_FileStream; q->fs = f; return true; } diff --git a/display_bar.c b/display_bar.c new file mode 100644 index 0000000..42c7de7 --- /dev/null +++ b/display_bar.c @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "display_bar.h" +#include "timerhelper.h" + +enum{ +#ifndef DEFAULT_PROGRESS_BAR_WIDTH + PROGRESS_BAR_WIDTH = 30, +#else + PROGRESS_BAR_WIDTH = DEFAULT_PROGRESS_BAR_WIDTH; +#endif +#ifndef DEFAULT_MAX_TERMINAL_ROW + MAX_TERMINAL_ROW = 1000, +#else + MAX_TERMINAL_ROW = MAX_TERMINAL_ROW; +#endif +}; + +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; +static raw_progress_bar_t scrolled = 1; + +void ready_progress_bar(){ + fflush(stdout); +} + +void lock_scrolled(){ + pthread_mutex_lock(&mutex); +} +void unlock_scrolled(){ + pthread_mutex_unlock(&mutex); +} + +void add_scrolled_unlocked(unsigned int i){ + scrolled += i; +} +raw_progress_bar_t create_raw_progress_bar(){ + int ret; + lock_scrolled(); + ret = scrolled; + //make space for progress bar + fprintf(stdout,"\n"); + scrolled++; + unlock_scrolled(); + return ret; +} + +void RawDisplayProgressBar(raw_progress_bar_t bar,size_t offset,size_t total,const char * filename){ + char buf[PROGRESS_BAR_WIDTH]; + size_t i; + double cur_progress = ((double)offset)/(double)total *100.f; + size_t cur_pos = (cur_progress / 100.0 * PROGRESS_BAR_WIDTH); //must be less than SIZE_MAX. other value is undefined behavior. + int pos; + struct winsize wnd_size; + if (offset == total) + cur_pos = PROGRESS_BAR_WIDTH; + for (i = 0; i < PROGRESS_BAR_WIDTH; i++){ + if (i < cur_pos) + buf[i] = '='; + else if(i == cur_pos) + buf[i] = '>'; + else buf[i] = '.'; + } + lock_scrolled(); + //if ioctl failed? what should i do... + ioctl(STDIN_FILENO,TIOCGWINSZ,(char *)&wnd_size); + if (wnd_size.ws_row < pos) return; + if (pos > MAX_TERMINAL_ROW) return;//optimization. + pos = scrolled - bar; + fprintf(stdout,"\x1b[%dA[%s]: %.2f%% %s bytes: %ld/%ld %d \x1b[%dB\r",pos,buf,cur_progress,filename,total,offset,pos,pos); + fflush(stdout); + unlock_scrolled(); +} +bool isatty_file(FILE * file){ + return isatty(fileno(file)); +} +void myd_perror(const char * msg){ + char buf[1024]; + int err = errno; + strerror_r(errno,buf,sizeof(buf)); + errno = err; + if (isatty(STDERR_FILENO)){ + lock_scrolled(); + scrolled++; + fprintf(stderr,"%s: %s\n",msg,buf); + unlock_scrolled(); + } +} + +void init_progress_bar(progress_bar_t * bar,int update_rate){ + memset(bar,0,sizeof(*bar)); + bar->bar = create_raw_progress_bar(); + bar->update_rate = update_rate; +} + +void DisplayProgressBar(progress_bar_t * bar,size_t offset,size_t total,const char * filename, bool sync){ + unsigned diff; + struct timespec ts,dif; + clock_gettime(CLOCK_REALTIME,&ts); + if (!sync){ + dif = timespec_sub(ts,bar->last_update); + diff = dif.tv_nsec / 1000000 + dif.tv_sec; + if (diff < bar->update_rate){ + return; + } + } + bar->last_update = ts; + RawDisplayProgressBar(bar->bar,offset,total,filename); +} \ No newline at end of file diff --git a/display_bar.h b/display_bar.h new file mode 100644 index 0000000..b83fea6 --- /dev/null +++ b/display_bar.h @@ -0,0 +1,37 @@ +#include +#include +#include + +#ifndef _DISPLAY_BAR_H_ +#define _DISPLAY_BAR_H_ + +typedef unsigned raw_progress_bar_t; + +void lock_scrolled(); + +void unlock_scrolled(); + +void ready_progress_bar(); + +void add_scrolled_unlocked(unsigned int i); + +raw_progress_bar_t create_raw_progress_bar(); + +void RawDisplayProgressBar(raw_progress_bar_t bar,size_t offset,size_t total,const char * filename); + +bool isatty_file(FILE * file); + +void myd_perror(const char * msg); + +typedef struct { + raw_progress_bar_t bar; + //millisecond unit + int update_rate; + struct timespec last_update; +} progress_bar_t; + +void init_progress_bar(progress_bar_t * bar,int update_rate); + +void DisplayProgressBar(progress_bar_t * bar,size_t offset,size_t total,const char * filename, bool sync); + +#endif \ No newline at end of file diff --git a/server.c b/server.c index 1ec080e..2f89ec8 100644 --- a/server.c +++ b/server.c @@ -17,6 +17,7 @@ #include "socket_wrapper.h" #include "simple_circular_buffer.h" #include "timerhelper.h" +#include "display_bar.h" #ifndef DEFAULT_MAX_LISTEN_SOCKET static const int MAX_LISTEN_SOCKET = 16; @@ -61,7 +62,7 @@ enum{ #endif }; #define USE_TRACE -#define USE_NO_QUEUE + /*======== *Operation @@ -80,11 +81,11 @@ int send_fail(int sock,const char * msg){ res.error_msg_size = strlen(msg); //os will be combining if tcp_autocorking emabled. if(send(sock,&res,sizeof(res),0) < 0){ - perror("error msg send"); + myd_perror("error msg send"); return -1; } if (send(sock,msg,res.error_msg_size,0) < 0){ - perror("error msg send"); + myd_perror("error msg send"); return -1; } return 0; @@ -100,7 +101,7 @@ int send_errno(int sock){ r.file_size = 0; r.error_msg_size = 0; if(send(sock,&r,sizeof(r),0)){ - perror("errno send"); + myd_perror("errno send"); return -1; } return 0; @@ -115,7 +116,7 @@ int read_request(int sock,uint8_t * buf,size_t bufsize){ ssize_t n = recv_until_byte(sock,&p,sizeof(p),TIMEOUT); if (n < 0){ if (n == -2) fprintf(stderr,"timeout!"); - else perror("receive fail"); + else myd_perror("receive fail"); return -1; } if(bufsize <= ((size_t)p.file_url_size) + sizeof(p) + 1){ @@ -132,7 +133,13 @@ int read_request(int sock,uint8_t * buf,size_t bufsize){ } n = recv_until_byte(sock,buf,p.file_url_size,TIMEOUT); buf[p.file_url_size] = '\0'; //truncate - fprintf(stdout,"str size: %d, request %s\n",p.file_url_size,buf); + if (isatty_file(stdout)){ + lock_scrolled(); + add_scrolled_unlocked(1); + fprintf(stdout,"str size: %d, request %s\n",p.file_url_size,buf); + unlock_scrolled(); + } + else fprintf(stdout,"str size: %d, request %s\n",p.file_url_size,buf); if(strchr((char *)buf,'/') != NULL){ send_fail(sock,"Illegal character /"); return -1; @@ -154,6 +161,7 @@ int send_response(int sock,int fd, uint8_t * buf, size_t bufsize){ struct stat st; off_t offset = 0; ssize_t readed = 0; + progress_bar_t pbar; r.res = RES_OK; r.err_number = 0; r.error_msg_size = 0; @@ -165,33 +173,37 @@ int send_response(int sock,int fd, uint8_t * buf, size_t bufsize){ } r.file_size = st.st_size; if(send(sock,&r,sizeof(r),0)<0){ - perror("send fail"); + myd_perror("send fail"); return -1; } + init_progress_bar(&pbar,10); #ifdef USE_SENDFILE while (r.file_size != offset) { size_t count = SEND_FILE_CHUNK_SIZE < (r.file_size - offset) ? SEND_FILE_CHUNK_SIZE : (r.file_size - offset); if((readed = sendfile(sock,fd,&offset,count)) < 0){ - perror("send file fail"); + myd_perror("send file fail"); return -1; } + DisplayProgressBar(&pbar,offset,r.file_size,"",false); } #else while (offset < r.file_size) { readed = bufsize < (r.file_size - offset) ? bufsize : r.file_size - offset; if(read(fd,buf,readed)<0){ - perror("send response read fail"); + myd_perror("send response read fail"); return -1; } if(send(sock,buf,readed,0)<0){ - perror("send response send fail"); + myd_perror("send response send fail"); return -1; } offset += readed; + usleep(500000); + DisplayProgressBar(&pbar,offset,r.file_size,"",false); } - + DisplayProgressBar(&pbar,offset,r.file_size,"",true); #endif return 0; } @@ -308,7 +320,6 @@ void * worker_proc(void * data){ struct timespec ts,ts_middle,ts_end; #endif for(;;){ - pthread_mutex_lock(&globalState.sock_mutex); while (queue_isempty(&globalState.socks)){ pthread_cond_wait(&globalState.ready,&globalState.sock_mutex); @@ -329,10 +340,15 @@ void * worker_proc(void * data){ clock_gettime(Trace_Timer_ID,&ts_end); struct timespec tophalf = timespec_sub(ts_middle,ts); struct timespec bottomhelf = timespec_sub(ts_end,ts_middle); - fprintf(stderr,"top : %ld ns, bottom : %ld ns\n",tophalf.tv_nsec,bottomhelf.tv_nsec); + if(isatty_file(stderr)){ + lock_scrolled(); + add_scrolled_unlocked(1); + fprintf(stderr,"top : %ld ns, bottom : %ld ns\n",tophalf.tv_nsec,bottomhelf.tv_nsec); + unlock_scrolled(); + } #endif if(close(csock) < 0) - perror("csock close error"); + myd_perror("csock close error"); } destory_worker_argument(args); return NULL; @@ -356,10 +372,16 @@ void * worker_proc(void * data){ clock_gettime(Trace_Timer_ID,&ts_end); struct timespec tophalf = timespec_sub(ts_middle,ts); struct timespec bottomhelf = timespec_sub(ts_end,ts_middle); - fprintf(stderr,"top : %ld ns, bottom : %ld ns\n",tophalf.tv_nsec,bottomhelf.tv_nsec); + if(isatty_file(stderr)){ + lock_scrolled(); + add_scrolled_unlocked(1); + fprintf(stderr,"top : %ld ns, bottom : %ld ns\n",tophalf.tv_nsec,bottomhelf.tv_nsec); + unlock_scrolled(); + } + else fprintf(stderr,"top : %ld ns, bottom : %ld ns\n",tophalf.tv_nsec,bottomhelf.tv_nsec); #endif if(close(csock) < 0) - perror("csock close error"); + myd_perror("csock close error"); destory_worker_argument(args); return NULL; } @@ -382,17 +404,17 @@ int main(int argc, const char *argv[]){ int d = parse_args(argc,argv,&binding_port_number); if(d != 0 ) return d; } - + ready_progress_bar(); sock = socket(AF_INET,SOCK_STREAM,0); atexit(safe_exit); if(sock < 0){ - perror("sock create fail"); + myd_perror("sock create fail"); return 1; } else { int option = 1; if(setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&option,sizeof(option)) < 0){ - perror("setsockopt"); + myd_perror("setsockopt"); } } bufsize = getBufferSizeFrom(sock); @@ -412,17 +434,23 @@ int main(int argc, const char *argv[]){ addr.sin_port = htons(binding_port_number); if(bind(sock, (struct sockaddr *)&addr,sizeof(addr)) < 0){ - perror("bind failed"); + myd_perror("bind failed"); return 1; } else { char ip_buf[INET_ADDRSTRLEN]; const char * msg = inet_ntop(AF_INET,&addr.sin_addr,ip_buf,sizeof(ip_buf)); assert(msg != NULL); - fprintf(stdout,"server bind on %s:%d\n",msg ,binding_port_number); + if(isatty_file(stdout)){ + lock_scrolled(); + add_scrolled_unlocked(1); + fprintf(stdout,"server bind on %s:%d\n",msg ,binding_port_number); + unlock_scrolled(); + } + else fprintf(stdout,"server bind on %s:%d\n",msg ,binding_port_number); } if(listen(sock,MAX_LISTEN_SOCKET) < 0){ - perror("listen failed"); + myd_perror("listen failed"); return 1; } @@ -430,7 +458,13 @@ int main(int argc, const char *argv[]){ { char ip_buf[INET_ADDRSTRLEN]; const char * msg = inet_ntop(AF_INET,&client_addr.sin_addr,ip_buf,sizeof(ip_buf)); - fprintf(stdout,"Connected on : %s:%d\n",msg == NULL ? "(null)" : msg , ntohs(addr.sin_port)); + if(isatty_file(stdout)){ + lock_scrolled(); + add_scrolled_unlocked(1); + fprintf(stdout,"Connected on : %s:%d\n",msg == NULL ? "(null)" : msg , ntohs(addr.sin_port)); + unlock_scrolled(); + } + else fprintf(stdout,"Connected on : %s:%d\n",msg == NULL ? "(null)" : msg , ntohs(addr.sin_port)); #ifdef USE_TRACE struct timespec ts; clock_gettime(Trace_Timer_ID, &ts);