📄 scheck.c
字号:
/* Copyright (c) 2000 Kevin Sullivan <nite@gis.net> * * Please refer to the COPYRIGHT file for more information. */#include <stdio.h>#include <ncurses.h>#include <stdlib.h>#include <time.h>#include <string.h>#include <sys/types.h>#include <unistd.h>#include <sys/socket.h>#include <errno.h>#include <sys/time.h>#include "defines.h"#include "codes.h"#include "colors.h"#include "scheck.h"#include "timer.h"#include "nap.h"#include "winio.h"#include "lists.h"#ifdef MEMWATCH #include "memwatch.h"#endifextern info_t info;extern int srch;extern int reconnect;/* the global connection list */sock_t *socklist = NULL;/* bandwidth observers for global uploads and global downloads */bandwidth_t bwup, bwdown;/* set by /quit to cause an immediate unconditional shutdown of nap, or by qevent() after a /tquit */int quit_now = 0;/* add a connection to end of global connection list. s is file descriptor, nm is name, t is type (S_R or S_W), func is handler function. d is left blank. nm is copied (strdup). Returns the new socklist element. Also, if this is an upload or download connection, notify server (ugly!) *//* Note that upload/dowload notification is done in event.c:dosend() and event.c:doget() for connections initiated by a remote client. */sock_t *addsock(int s, char *nm, unsigned char t, int (*func)(WINDOW *, sock_t *)){ sock_t *cur, *sv; /* create a new socket */ cur = (sock_t *)malloc(sizeof(sock_t)); cur->fd = s; cur->t = t; cur->socknm = strdup(nm); cur->func = func; cur->next = NULL; cur->dxx = NULL; cur->shared_to_send = NULL; cur->utask = NULL; cur->dtask = NULL; cur->btask = NULL; cur->bwlimit = 0; /* an add it to the list */ list_append(sock_t, socklist, cur); /* if it's an upload or a download, notify the server */ sv = findsock("server"); if (sv) { if (cur->socknm[0] == 'u' && cur->socknm[1] == ' ') { sendpack(sv->fd, NAP_UP, NULL); } else if (cur->socknm[0] == 'd' && cur->socknm[1] == ' ') { sendpack(sv->fd, NAP_DOWN, NULL); } } return cur;}/* delete a connection. It is uniquely identified by its file descriptor s. This routine also notifies the server if an upload or download is being shut down (this is ugly, since this should rather be done in connection with the task structure, not the connection structure). It also sets srch to 0 in case we are deleting the server connection. It also shuts down the socket of the connection, and closes it, if it is indeed a socket connection (file descriptor >2). */void delsock(int s){ sock_t *cur, *sv; library_t *l; /* unlink the first connection whose fd is s, if any */ list_unlink_cond(sock_t, socklist, cur, cur->fd == s); if (!cur) { return; } sv = findsock("server"); if (sv) { if (cur->socknm[0] == 'd' && cur->socknm[1] == ' ') { sendpack(sv->fd, NAP_DOWNDONE, NULL); } else if (cur->socknm[0] == 'u' && cur->socknm[1] == ' ') { sendpack(sv->fd, NAP_UPDONE, NULL); } } if (!strcmp(cur->socknm, "server")) srch = 0; if (cur->shared_to_send) { list_forall_unlink(l, cur->shared_to_send) { free(l->lfn); free(l->hash); free(l); } } free(cur->socknm); free(cur); /* Note: a connection handler may not close the socket before or * after the call to delsock(), since it is always closed here, and * it can be harmful (segfault) to close a file twice. */ if (s > 2) { shutdown(s, 2); close(s); } return;}/* finds a connection by name in the connection list */sock_t *findsock(const char *nm){ sock_t *elt; list_find(elt, socklist, !strcasecmp(elt->socknm, nm)); return(elt);}/* finds a connection by file descriptor in the connection list */sock_t *findsockfd(int fd){ sock_t *elt; list_find(elt, socklist, elt->fd == fd); return(elt);}/* prints the list of connections to the specified window. */void psocks(WINDOW *win){ sock_t *cur; wp(win, "fd | nm\n"); for (cur=socklist;cur!=NULL;cur=cur->next) wp(win, "%2d %s\n", cur->fd, cur->socknm); drw(win);}/* the next few functions are for bandwidth limiting. The idea is that a bandwidth "observer" is created for each bandwidth limit. Every time bytes travel through the connection, the observer is informed. The observer calculates, on demand, the number of microseconds one has to wait until the next packet may be sent/received. *//* register n bytes that have been sent and are subject to bw */void bandwidth_register(bandwidth_t *bw, int n) { bw->bytes += n;}/* initialize bw */void bandwidth_init(bandwidth_t *bw) { bw->bandwidth = 0;}/* return the time in microseconds that this connection has to sleep in order to satisfy its bandwidth constraint. curbw is the bandwidth currently requested by the user; note that this may be different from bw->bandwidth if the requested bandwidth changed, or the very first time this is called for a connection. tp is a pointer to a timeval structure that holds the current time. */long bandwidth_waittime(bandwidth_t *bw, int curbw, struct timeval *tp) { long ms; if (curbw < 0) curbw = 0; /* First check if the requested bandwidth differs from the stored one. If yes, re-calculate state. */ if (bw->bandwidth != curbw) { if (bw->bandwidth == 0) { bw->bandwidth = curbw; bw->time0 = tp->tv_sec; bw->bytes = tp->tv_usec / 1000 * curbw; } else { bw->bytes = (bw->bytes / bw->bandwidth) * curbw; bw->bandwidth = curbw; } } if (curbw==0) return 0; /* no bandwidth limit requested */ /* next, adjust the time0 parameter to tp->tv_sec, i.e., to the integer component of the current time. */ if (tp->tv_sec != bw->time0) { bw->bytes -= curbw * 1000 * (tp->tv_sec - bw->time0); bw->time0 = tp->tv_sec; } /* calculate the number of microseconds this connection needs to sleep */ ms = bw->bytes / curbw * 1000 - tp->tv_usec; /* if we are more than a second behind, forfeit anything beyond one second of credit. I.e., normally we get credit for being "too slow" (and can make up for it later), but at most one second's worth of such credit is allowed */ if (ms < -1000000) { bw->bytes = (tp->tv_usec / 1000 - 1000) * curbw; ms = bw->bytes / curbw * 1000 - tp->tv_usec; } return ms;}/* sockfunc: this is the main event loop of the nap program. It is * here that we wait for network activity and then tend to the various * different connections (including user input). In addition, we call * tevent() (in timer.c) once a second or so, to take care of * scheduled events. * * This function is called precisely once, from nap.c:main(). Once * we leave here, nap shuts down and terminates. * * up is the input window (equal to the global variable winput), and * win is the output window of the terminal interface (equal to the * global variable wchan). Why not just refer to the global variables, * as do some of the procedures that are called from here? * * The connection handlers (cur->func) are always supposed to return 1 * (the return value is not currently checked). If a connection * handler wants to shut down, it needs to call delsock(). * **/void sockfunc(WINDOW *win, WINDOW *up){ fd_set fs, fw; /* sets of file descriptors to watch, see "man select" */ int r; sock_t *cur; struct timeval sec; /* note originally used <time.h> timespec here instead of <select.h> timeval - this was a type error */ long timeout, msup, msdown; struct timeval tv; library_t *lib; bandwidth_init(&bwup); bandwidth_init(&bwdown); while (1) { dochecks(); /* notify terminal that it has been resized, if necessary. */ drw(up); /* redraw input window to position cursor */ /* if the --autorestart option is set, and there is no server, try to reconnect to a server. */ if (info.autorestart == 1) { for (cur=socklist;cur!=NULL;cur=cur->next) { if (!strcasecmp(cur->socknm, "server")) break; } if (cur==NULL) { sleep(1); /* sleep to avoid excessive load in case of infinite loop */ wp(win, "Connection to server lost, reconnecting...\n"); drw(win); dreconnect(-1, "reconnect", NULL, 0, win); } } /* we return if this was requested by the /quit command or by /tquit in conjunction with qevent() */ if (quit_now) { goto quit; } /* we reconnect if this was requested by a SIGUSR1 signal */ if (reconnect) { reconnect = 0; dreconnect(-1, "reconnect", NULL, 0, win); } /* determine current time */ gettimeofday(&tv, NULL); msup = bandwidth_waittime(&bwup, info.bandwidthup, &tv); msdown = bandwidth_waittime(&bwdown, info.bandwidthdown, &tv); timeout = 1000000; /* let select() time out after at most this many microsecs */ /* let fs be the set of "read" file descriptors, and fw the list of "write" file descriptors, from the connection list */ FD_ZERO(&fs); FD_ZERO(&fw); for (cur=socklist;cur!=NULL;cur=cur->next) { if (cur->bwlimit) { /* bandwidth limiting code */ int ms; /* calculate the sleep time for this connection */ if (cur->t == S_R) { ms = bandwidth_waittime(&cur->bw, info.bandwidthdownconn, &tv); if (msdown > ms) { ms = msdown; } } else { ms = bandwidth_waittime(&cur->bw, info.bandwidthupconn, &tv); if (msup > ms) { ms = msup; } } /* if ms<=0, activity is immediately allowed and we proceed to watch this connection in the "select" call below. Else if ms>0, activity is not allowed, but we make sure "select" will time out after ms microseconds so that this connection can be added at that time. */ if (ms>0) { if (timeout > ms) { timeout = ms; } continue; } } if (cur->t == S_R) FD_SET(cur->fd, &fs); else if (cur->t == S_W) FD_SET(cur->fd, &fw); /* special case: the server is normally polled for reading, but if we have stuff scheduled to send to it, we poll for writing as well */ if (strcmp(cur->socknm, "server")==0 && cur->shared_to_send) { FD_SET(cur->fd, &fw); } } /* prepare timeout, then wait for activity on connections. On fs connections, wait for characters to be available for reading. On fw connections, wait for ability to write. */ sec.tv_sec = timeout / 1000000; sec.tv_usec = timeout % 1000000; r = select(FD_SETSIZE, &fs, &fw, NULL, &sec); /* timeout guarantees calling the following at least once per second */ tevent(); /* check for timer events */ qevent(); /* check queues */ /* if there is an error, print it, but then continue. However, EINTR means select was interrupted. This happens e.g. when the user resizes the window. */ if (r == -1) { if (errno != EINTR) { wp(win, ""RED"* Error while watching connections: %s"WHITE"\n", \ strerror(errno)); drw(win); } continue; } /* if there was no network activity, continue main loop */ if (r == 0) continue; /* else do something for every connection that had activity. Essentially, just call the handler (cur->func) for that connection. */ for (cur=socklist;cur!=NULL;cur=cur->next) { if (cur->t == S_R && FD_ISSET(cur->fd, &fs)) { FD_CLR(cur->fd, &fs); cur->func(win, cur); if (quit_now) { goto quit; } cur=socklist; /* need to start from beginning since sockets might have been deleted in the meantime */ } else if (cur->t == S_W && FD_ISSET(cur->fd, &fw)) { FD_CLR(cur->fd, &fw); cur->func(win, cur); if (quit_now) { goto quit; } cur=socklist; } else if (strcmp(cur->socknm, "server")==0 && cur->shared_to_send && FD_ISSET(cur->fd, &fw)) { FD_CLR(cur->fd, &fw); /* send scheduled stuff to server */ /* unlink first element */ lib = cur->shared_to_send; cur->shared_to_send = lib->next; r = sendpack(cur->fd, NAP_SFILE, "\"%s\" %s %i %i %i %i", lib->lfn, lib->hash, lib->sz, lib->bitrate, lib->freq, lib->len); free(lib->lfn); free(lib->hash); free(lib); cur=socklist; /* not strictly necessary, but just in case... */ } } } /* end of loop not reachable */ quit: /* we get here if the user issues the /quit command. */ while (socklist!=NULL) delsock(socklist->fd); return;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -