#include "ichatserv.h" // options stored here (not all, look for listener.c also) int OPT_REUSEADDR = 1; int OPT_USERPORT = 6666; int OPT_SERVERPORT = 6667; char OPT_LOGFILE[256]; char OPT_PIDFILE[256]; // some storages... make it global, as we use it cleanup routine // make all vars clean by default static int usersocket = -1; static int serversocket = -1; static struct sockaddr_in useraddr; static struct sockaddr_in serveraddr; // support for signal handlers... int need2restart = 0; int need2exit = 0; int restarted = 0; // lists CLIST* userlist = NULL; CLIST* serverlist = NULL; CLIST* timelist = NULL; // static void sig_hup_hndlr( int signo ) { // set to ignore all incoming signals // until we enable it... signal( SIGHUP, SIG_IGN ); // print2log( "SIGNAL: HUP" ); need2restart = 1; } static void sig_segv_hndlr( int signo ) { // again, set ignorance... // but i HOPE that we will never catch this signal signal( SIGSEGV, SIG_IGN ); // TODO - why ? // signal(SIGSEGV, sig_segv_hndlr); // signal( SIGHUP, SIG_IGN ); // print2log( "Segmentation failed signal received!!! Atempting to restart programm." ); print2log( "SIGNAL: SEGV" ); // need2restart = 1; need2exit = 1; } static void sig_term_hndlr( int signo ) { // restore handler signal( SIGTERM, sig_term_hndlr ); // print2log( "SIGNAL: TERM" ); need2exit = 1; } // some further definitions... void* client_thread( void* parameter ); // client thread int client_accept( int s, int user ); // accept new client int pollall( void ); // oki. main and its support void cleanup( void ) { // close primary socket... if( usersocket != -1 ) { close( usersocket ); usersocket = -1; } if( serversocket != -1 ) { close( serversocket ); serversocket = -1; } // free memory, allocated for users... if( userlist ) { clist_clean( userlist ); free( userlist ); userlist = NULL; } if( serverlist ) { clist_clean( serverlist ); free( serverlist ); serverlist = NULL; } if( timelist ) { // oki, clean the memory CLISTITEM* temp; while( (temp = timelist->head) ) { timelist->head = temp->next; time_clean( (SERVERTIME*) temp ); } // clist_clean( timelist ); free( timelist ); timelist = NULL; } // forgotten way... // if options changes the file, we need to close previous opened close_log(); // delete old pid file. // as options can be changed, we need to recreate it unlink( OPT_PIDFILE ); } // returns socket handke or -1 on error int opensocket( int port, struct sockaddr_in* addr ) { int mysocket = -1; // oki... open the socket for listening if( (mysocket = socket( AF_INET, SOCK_STREAM, 0 )) == -1 ) { print2log( "ERROR: Socket creating error (port: %i): %s", port, strerror( errno ) ); return -1; } // setup socket for reusing address if specified // wow ! it works! // oki. after having chatserver shutdowned, thereare pending client connection // linger does not help, so they are hanging around about minute or two (dead timeout) // but the server could not be restarted because socket address is in use... // settings SO_REUSEADDR will allow us to open new listening sockets // in case of lost pending connections... but it will not allow us to run chat again :) if( OPT_REUSEADDR ) { int opt = 1; if( setsockopt( mysocket, SOL_SOCKET, SO_REUSEADDR, & opt, sizeof( opt ) ) == -1 ) { print2log( "ERROR: Call to setsockopt failed (port: %i): %s", port, strerror( errno ) ); close( mysocket ); return -1; } } // initialize data for socket listening... addr->sin_family = AF_INET; addr->sin_addr.s_addr = INADDR_ANY; addr->sin_port = htons( port ); if( bind( mysocket, (struct sockaddr*) addr, sizeof( *addr ) ) == -1 ) { print2log( "ERROR: Error binding socket (port: %i): %s", port, strerror( errno ) ); close( mysocket ); return -1; } // why ? we call any.. //if( !restarted ) printf( "iChat Server READY! to listen on %s\n", inet_ntoa( serv_addr.sin_addr ) ); if( listen( mysocket, 5 ) == -1 ) { print2log( "ERROR: Error listening socket (port: %i): %s", port, strerror( errno ) ); close( mysocket ); return -1; } // return mysocket; } int main( int argc, char** argv ) { // setup signal handlers... // TODO - understand it signal( SIGHUP, sig_hup_hndlr ); signal( SIGTERM, sig_term_hndlr ); signal( SIGPIPE, SIG_IGN ); signal( SIGINT, SIG_IGN ); signal( SIGALRM, SIG_IGN ); signal( SIGSEGV, sig_segv_hndlr ); // on the first we run, we need to check the // if we are runned under init daemon.... // ERROR // vlad have called getpid function... may be we need to call getppid ? if( getppid() != 1 ) { int forker; // ignore some signals... // TODO - why we do it here ? // why not after fork ? signal( SIGTTOU, SIG_IGN ); signal( SIGTTIN, SIG_IGN ); signal( SIGTSTP, SIG_IGN ); // fork returns 0 in the child thread or -1 upon error // otherwise it will return PID of new process if( (forker = fork()) == -1 ) return 1; else if( forker != 0 ) // i.e. we are in the caller return 0; // set us as a process group leader... make anything else... stupid call. why we can't use CreateProcess // with well documented API ? setsid(); } restart: // cleanup restart state... // its easy need2restart = 0; // oki. we can get here throught restart or throught initial program execution // in case of restart, we need to do some cleanup work... // i.e. deleting temp files or freeing memory... // if( restarted ) // cleanup(); // shit fuck // if we do cleanup upon goto restart, we don't have to call cleanup here // we almost don't need in the restarted variables, as only use is to print started or restarted messages... // shit fuck again // expensive usage of restart detected in the options.c // read options from LOGFILE and parse them into options var... // on error (bad cause - returns TRUE on error) we will return from process immediatly... // if main() thread dies all others die also if( ! get_options() ) return 1; // try to initialize log file // if not, fail... if( ! open_log() ) return 1; // DONE - make it like dynamic list. we don't need the limit... etc userlist = malloc( sizeof( CLIST ) ); if( ! userlist ) { print2log( "ERROR: Can't allocate memory for userlist" ); cleanup(); return 1; } clist_init( userlist ); // serverlist = malloc( sizeof( CLIST ) ); if( ! serverlist ) { print2log( "ERROR: Can't allocate memory for serverlist" ); cleanup(); return 1; } clist_init( serverlist ); // timelist = malloc( sizeof( CLIST ) ); if( ! timelist ) { print2log( "ERROR: Can't allocate memory for timelist" ); cleanup(); return 1; } clist_init( timelist ); // do something with signals // as i undertstand, we restore handlers here // after restart procedure // TODO // ERROR signal( SIGHUP, sig_hup_hndlr ); signal( SIGSEGV, sig_segv_hndlr ); // check for port equality or misvalidty, it will also include case when both ports are equal to -1 if( OPT_USERPORT == OPT_SERVERPORT ) { print2log( "ERROR: Invalid port values specified" ); cleanup(); return 1; } // if( OPT_USERPORT > 0 ) // i.e. not 0 and not -1 { if( (usersocket = opensocket( OPT_USERPORT, & useraddr )) == -1 ) { cleanup(); return 1; } } if( OPT_SERVERPORT > 0 ) { if( (serversocket = opensocket( OPT_SERVERPORT, & serveraddr )) == -1 ) { cleanup(); return 1; } } // generate server name, if not already set if( ! OPT_SERVERNAME[0] ) { // query system for max 10 interface addresses struct ifconf conf; struct ifreq req[10]; int last; char hostname[128]; // conf.ifc_len = sizeof( req ); conf.ifc_req = req; if( ioctl( serversocket, SIOCGIFCONF, & conf ) == -1 ) { print2log( "ERROR: Unable to query interface addresses: %s", strerror( errno ) ); cleanup(); return 1; } // get interface address last = conf.ifc_len / sizeof( req[0] ); if( last == 0 ) { print2log( "ERROR: There are no available interface addresses" ); cleanup(); return 1; } if( last > 10 ) last = 10; last--; // get host name... if( gethostname( hostname, sizeof( hostname )-1 ) == -1 ) { print2log( "ERROR: Can't obtain host name: %s", strerror( errno ) ); cleanup(); return 1; } // generate name... sprintf( OPT_SERVERNAME, "%s/%s", inet_ntoa( ((struct sockaddr_in*) & req[last].ifr_addr)->sin_addr ), hostname ); print2log( "INFO: Generated server name is: %s", OPT_SERVERNAME ); } // shit fuck, mother crackers... // we dump our pid to the file, so anybody who wants to restart or destroy us can do it easily // more over... we need to do unlink to this file in case of exit out... // do it always... as signal of successful initialization { FILE* pidfile = fopen( OPT_PIDFILE, "w+t" ); if( pidfile ) { fprintf( pidfile, "%u\n", getpid() ); fclose( pidfile ); } } // print2log( "Server %s successfully", ( restarted ) ? "restarted" : "started" ); if( OPT_USERPORT != -1 ) print2log( "Listening for users: %s:%i", inet_ntoa( useraddr.sin_addr ), ntohs( useraddr.sin_port ) ); if( OPT_SERVERPORT != -1 ) print2log( "Listening for servers: %s:%i", inet_ntoa( serveraddr.sin_addr ), ntohs( serveraddr.sin_port ) ); //print2log( "Ready for queries" ); // we can set it here... as there is no way to get throught this code without restating... restarted = 1; // { int pollresult = pollall(); if( pollresult == -1 ) // force restart { need2restart = 1; // allow threads to break upon restart request clist_waitempty( userlist ); clist_waitempty( serverlist ); cleanup(); goto restart; } else { need2exit = 1; clist_waitempty( userlist ); // like it was above, but on exit request clist_waitempty( serverlist ); cleanup(); return pollresult; } } // trunk... return 0; } // poll for connections.. // returns 0 or 1 for exit request (success and bad corresponfing), and -1 upon restart request int pollall() { // for now, allow only to items to be used... struct pollfd pfd[2]; int pfdcount = 0; // if( OPT_USERPORT != -1 ) pfd[pfdcount++].fd = usersocket; if( OPT_SERVERPORT != -1 ) pfd[pfdcount++].fd = serversocket; // while( 1 ) { // use algo for accepting connections // wait for connection, if ready - accept it, otherwise - check for need* vars int poller, cnt; // reset poll state for( cnt = 0; cnt < pfdcount; cnt++ ) { pfd[cnt].events = POLLIN; pfd[cnt].revents = 0; } // original idea - poll the socket every 100 ms. // but after checking, it was discovered that poll breaks upon // signal and returns EINTR... so we can wait for infinite timeout // and react only in signal errors // QUESTION - what if many threads are active ? which one will catch the signal ? // suppose that we need to break sometimes... if( (poller = poll( pfd, pfdcount, -1 )) == -1 && errno != EINTR ) // error condition { print2log( "ERROR: Can't poll sockets: %s", strerror( errno ) ); return 1; } // check for need2exit signal... // i still can't understand the behaviour with signals and threads... if( need2exit ) { print2log( "INFO: Detected EXIT request" ); return 0; } // bad. we received restart signal... if( need2restart ) { print2log( "INFO: Detected RESTART request" ); return -1; // force restart } // oki, we are here... so nothing happens, but check for connection // if poller (i.e. number of structures accepted) equals 1 if( poller > 0 ) // i.e. at least on socket has accepted { // scan all entries... may be in reverse ? :) for( cnt = 0; cnt < pfdcount; cnt++ ) { if( pfd[cnt].revents & POLLIN ) { // check for mode... if( pfd[cnt].fd == usersocket ) { // try to accept user connection // if can't break all, as its fatal error if( ! client_accept( usersocket, 1 ) ) return 1; } else if( pfd[cnt].fd == serversocket ) { if( ! client_accept( serversocket, 0 ) ) return 1; } } } } } // we should never get here return 0; } // accept new user connection, coming from mainsocket // returns 1 upon success and 0 on error int client_accept( int s, int user ) { pthread_t thread; // new thread handle struct sockaddr_in clientaddr; // client address info int clientaddr_len; int ns; // storage for the new socket handle CLIENT* client; // new user information... // clear client socket address // TODO - why ? memset( & clientaddr, 0, (clientaddr_len = sizeof( clientaddr )) ); // accept connection... if( (ns = accept( s, (struct sockaddr*) & clientaddr, & clientaddr_len )) == -1 ) { print2log( "ERROR: Can't accept client connection (%i): %s", user, strerror( errno ) ); return 0; } // dump client connection... print2log( "INFO: New client connection (%i) from %s:%i", user, inet_ntoa( clientaddr.sin_addr ), ntohs( clientaddr.sin_port ) ); // DONE // WARN all errors which can occur down will not break program execution // ad they are fatal only for // create user entry... if( (client = client_create( ns, & clientaddr, ( user ) ? CLIENT_USER : CLIENT_INSERVER )) ) { // StaX - 29/01/2002 - some rearrangements with user_clean and user_add /* // add user to the list // so it can receive broadcasts... if enabled ulist_add( user ); // try to create new thread for incoming connection handler if( pthread_create( & thread, NULL, user_thread, (void*) user ) != 0 ) { print2log( "ERROR: Can't create user thread" ); // remove user from the list, as thread creation failed // this will clean all memory allocated and aslo close accepted socket ulist_remove( user ); user_clean( user ); }*/ if( pthread_create( & thread, NULL, client_thread, (void*) client ) != 0 ) { print2log( "ERROR: Can't create client thread (%i)", user ); client_clean( client ); } else clist_add( ( user ) ? userlist : serverlist, client ); } else // just close lost socket close( ns ); // return success return 1; } // used as thread procedure for each client // take new USER structure as paramater void* client_thread( void* parameter ) { CLIENT* client = (CLIENT*) parameter; // ALERT // make this thread detached... // it means that any information related to the thread will be immedialty released after thread complition pthread_detach( pthread_self() ); // setup socket to nonblocking mode // this will immediatly break recv ops or recv ops... // fcntl( clientsocket, F_SETFL, O_NONBLOCK ); // code and loop begins here while( client_process( client ) ) ; // id did not want to make a very big if for checking initiali memory allocation // by the way, if is a some wrapper against goto, is't it ? // make some cleanup code if( client->identifier ) print2log( "INFO: Close connection for %s (reason: %s)", client->identifier, client->reason ); else print2log( "INFO: Close unidentified connection (reason: %s)", client->reason ); // clist_remove( ( client->type == CLIENT_USER ) ? userlist : serverlist, client ); client_clean( client ); // return NULL; }