/* * * Mon Apr 30 21:12:19 DST 2001 * Sample tcp server using select() * * :set tabstop=4 * * Tested on: * * FreeBSD 4.2 ix86 gcc 2.95.2 * SunOS 5.7 Sparc gcc 2.95.2 * SunOS 5.8 Sparc WorkShop Compilers 5.0 * IRIX 6.2 mips MIPSpro Compilers: Version 7.2.1.2m * Tru64 5.0 alpha Compaq C V6.1-011 * Linux RH 7.0 alpha gcc 2.96 * Linux RH 6.2 ix86 gcc 2.95.2 * HP-UX B.11.00 PA-RISC HP C/ANSI C Compiler B.11.01.06 * MacOS X 10.2.4 PPC gcc 1175 based on gcc 3.1 20020420 * * Tue Sep 23 21:30:57 DST 2003 * Added UDP server, tested on IRIX/cc; Linux/gcc */ static char __ident[] = "@(#)SPDsoft, Wed May 2 18:55:37 DST 2001"; #define VERS_STR ((char*)&__ident[4]) #include #include /* va_ */ #include /* atoi */ #if defined(__MACH__) && defined(__APPLE__) #include #endif #include /* getpid */ #include /* getpid */ #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __sgi # define HAVE_DAEMONIZE #endif #ifndef HAVE_DAEMONIZE /* _daemonize(3C) flags */ #define _DF_NOFORK 0x1 /* don't fork */ #define _DF_NOCHDIR 0x2 /* don't chdir to / */ #define _DF_NOCLOSE 0x4 /* close no files */ int my_daemonize(int flags, int fd1, int fd2, int fd3); #define _daemonize my_daemonize #endif #define HAVE_SETSID 1 /* irix, linux, solaris */ #define HAVE_STRERROR 1 #ifndef HAVE_STRERROR # define strerror(e) sys_errlist[(e)] #endif #ifndef _SYSV_SIGNALS # define _BSD_SIGNALS #endif #ifdef _BSD_SIGNALS # define sigset signal #endif #define IPL 32 #define MAXS 100 #ifndef DEFAULT_TPORT #define DEFAULT_TPORT 5004 #endif /* * Globals */ int done; typedef struct { char my_ip[IPL]; int port_d, port_s; } server_data_struct; server_data_struct server_data[MAXS]; typedef struct { unsigned int port; int debug; int udp; char *logfile; FILE *f; } prefs_struct; prefs_struct gprefs; /* * Prototypes */ void clean( int i ); void usage(char *name); void debug_msg(const char *fmt, ...); /* */ #define request_is(s1,s2) ( (s1[0]==s2[0])&&\ (s1[1]==s2[1])&&\ (s1[2]==s2[2])&&\ (s1[3]==s2[3]) ) void usage(char *name) { fprintf(stderr, "usage: %s [-p port] [-l logfile] [-d] [-v]\n",name); fprintf(stderr, "-d: debug\n"); fprintf(stderr, "-v: version\n"); exit(-1); } #define perror(e) debug_msg("%s (%s)\n", e, strerror(errno)) void debug_msg(const char *fmt, ...) { va_list vl; if ( NULL != gprefs.f) { va_start(vl,fmt); vfprintf(gprefs.f,fmt,vl); va_end(vl); fflush(gprefs.f); } } int main(int argc, char *argv[]) { #define BS 1000 double n=0; unsigned long n_p=0; long nbytes; int s; int sockfd=0; struct sockaddr_in server, client; int clientlen=sizeof(client); int fd; unsigned char buf[BS]; uint32_t sum; unsigned char *req; int err; struct timeval tv; fd_set mask, rmask; int nfound; int maxfd; int reuse=1; int option; gprefs.port=DEFAULT_TPORT; gprefs.debug=0; gprefs.udp = 0; gprefs.logfile=NULL; gprefs.f=stderr; while ( (option=getopt(argc,argv,"hdp:l:vu")) != EOF ) { switch(option) { case 'u': gprefs.udp = 1; break; case 'p': gprefs.port = (unsigned int) atoi(optarg); break; case 'l': gprefs.logfile = optarg; break; case 'd': gprefs.debug = 1; break; case 'v': fprintf(stdout, "%s\n",VERS_STR); exit(0); break; case 'h': default: usage(argv[0]); break; } } tv.tv_sec = 60; tv.tv_usec = 500000; memset(&server,0,sizeof(server)); if ( !gprefs.debug ) { if ( -1 == _daemonize(_DF_NOCHDIR, gprefs.f != NULL ? fileno(gprefs.f) : -1, -1,-1)) perror("Can't daemonize"); } server.sin_family = AF_INET; server.sin_port = htons(gprefs.port); server.sin_addr.s_addr = htonl(INADDR_ANY); if ( ( SIG_ERR == sigset(SIGTERM, clean)) || ( SIG_ERR == sigset(SIGINT, clean)) ) perror("signal"); if ( (s=socket(AF_INET,gprefs.udp?SOCK_DGRAM:SOCK_STREAM,0)) == -1 ) { perror("Can't get socket"); exit(1); } /* debug_msg("open socket %d\n", s);*/ if (setsockopt(s, SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(int)) == -1) { perror("setsockopt"); } if( bind( s, (struct sockaddr *)&server, sizeof(server)) != 0 ) { perror("bind failed"); exit(1); } if ( NULL != gprefs.logfile ) { if ( NULL == ( gprefs.f = fopen(gprefs.logfile, "wb"))) { perror(optarg); gprefs.logfile = NULL; } } done=0; FD_ZERO(&mask); FD_SET(s, &mask); maxfd=s; if (!gprefs.udp) { if ( listen( s, 5 ) != 0 ) { perror("Can't listen"); exit(1); } debug_msg("PID %d listening on %d\n", getpid(), ntohs(server.sin_port)); } for(;!done;) { rmask=mask; nfound=select(maxfd+1, &rmask, NULL, NULL, &tv); /* Linux modifies tv, so we need to reset it */ tv.tv_sec = 60; tv.tv_usec = 500000; if (nfound<0) { if ( EINTR == errno ) { debug_msg("Interrupted system call\n"); done=1; break; } /* * An error occurred during the select, or * the select timed out. */ if ( 0 != errno ) { perror("select"); done=1; } } if (nfound==0) { continue; } if (FD_ISSET(s, &rmask)) { done=0; clientlen=sizeof(client); if (!gprefs.udp) { debug_msg("%d accepting\n", getpid()); if ( (sockfd = accept( s, (struct sockaddr *)&client,&clientlen)) < 0 ) { if ( errno != EINTR ) { perror("accept"); done=1; } else { debug_msg("accept interrupted\n"); done=1; } } else { debug_msg("- %d: accepted from %s\n", sockfd, inet_ntoa(client.sin_addr)); fflush(stderr); FD_SET(sockfd, &mask); if (sockfd > maxfd) maxfd = sockfd; FD_CLR(s, &rmask); } } else { debug_msg("%d receiving\n", getpid()); nbytes = recvfrom( s, buf,sizeof(buf)-1,0, (struct sockaddr *)&client,&clientlen); debug_msg("%d received %d bytes (%s) from %s via fd %d\n", getpid(), nbytes, buf, inet_ntoa(client.sin_addr), s); debug_msg("%d sending %d bytes (%s) to %s via fd %d\n", getpid(), 6, "OK ", inet_ntoa(client.sin_addr), s); sendto( s, "OK ", 6, 0, (struct sockaddr *) &client, sizeof(struct sockaddr_in)); FD_ZERO(&mask); FD_SET(s, &mask); } } for ( fd=0; !gprefs.udp && fd<=maxfd; fd++) { if (FD_ISSET(fd, &rmask)) { nbytes = recv(fd,buf,sizeof(buf)-1,0); if ( nbytes < 0 ) { perror("read"); } if ( nbytes <= 0 ) { debug_msg("- %d: EOF\n", fd); FD_CLR(fd,&mask); if ( -1 == shutdown(fd,2)) perror("shutdown"); else debug_msg("- %d: shutdown\n",fd); if ( -1 == close(fd)) perror("close"); else debug_msg("- %d: closed\n", fd); done = 0; continue; } req = (unsigned char *) buf; debug_msg("- %d: received request (%.4s)\n", fd, buf); /* [...] */ if (request_is(req,"QUIT")) { /* Quit */ write(fd,"OK ",6); FD_CLR(fd,&mask); if ( -1 == shutdown(fd,2)) perror("shutdown"); else debug_msg("- %d: shutdown\n",fd); if ( -1 == close(fd)) perror("close"); else debug_msg("- %d: shutdown\n",fd); } else { debug_msg("- %d: Unknown request (%.4s)\n", fd, buf); write(fd,"ERUNKN",6); break; } fflush(stdout); n += nbytes; n_p += 1; } } } debug_msg("Rec: %.0f bytes, %lu packets\n",n,n_p); for ( fd=maxfd; fd>0; fd--) { if ( -1 == shutdown(fd,2)) perror("shutdown"); else debug_msg("fd %d shutdown\n",fd); if ( -1 == close(fd)) perror("close"); else debug_msg("fd %d closed\n",fd); } exit(errno); return(errno); /* Keep compiler happy */ } /************************************************************************/ void clean( int sig ) { if ( sig != SIGSEGV) { if ( SIG_ERR == sigset(sig, clean)) perror("clean:signal"); debug_msg("%d a escaparrar (%d)\n", getpid(), sig); fflush(stderr); done=1; } } /************************************************************************/ #ifndef HAVE_DAEMONIZE int my_daemonize(int flags, int fd1, int fd2, int fd3) { int status=0; int fd; /* debug_msg("daemonizing\n");*/ if (!( flags & _DF_NOFORK )) { /* Fork and exit in the parent to disassociate from the * current process group and become the leader of a new * process group. */ if ( -1 == (status = fork())) { perror("cant fork"); exit(1); } else if ( status > 0 ) { _exit(0); } #if HAVE_SETSID setsid(); #endif if ( -1 == (status = fork())) { perror("cant fork again"); exit(1); } else if ( status > 0 ) { _exit(0); } } #ifdef TIOCNOTTY if ( 0 < (fd = open("/dev/tty", O_RDWR))) { if (ioctl(fd, TIOCNOTTY, NULL) < 0) { perror("can't disassociate from the terminal"); status=-1; } close(fd); } #endif /* TIOCNOTTY */ if (!(flags & _DF_NOCHDIR)) { if (chdir("/.") < 0) { perror("can't chdir to /."); status=-1; } } if (!(flags & _DF_NOCLOSE)) { long l; int i; l=sysconf(_SC_OPEN_MAX); while(l-->0) { if ((l!=fd1)&&(l!=fd2)&&(l!=fd3)) close(l); } if ( -1 == ( i = open( "/dev/null", O_RDWR))) { perror("/dev/null"); status=-1; } else { if ((1!=fd1)&&(1!=fd2)&&(1!=fd3)) dup(i); if ((2!=fd1)&&(2!=fd2)&&(2!=fd3)) dup(i); } } return(status); } #endif