//----------------------------------------------------------------------------- // Copyright © 2003 - Philip Howard - All rights reserved // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. //----------------------------------------------------------------------------- // package libh/daemon // purpose Support daemon startup // homepage http://libh.slashusr.org/ //----------------------------------------------------------------------------- // author Philip Howard // email libh at ipal dot org // homepage http://phil.ipal.org/ //----------------------------------------------------------------------------- // This file is best viewed using a fixed spaced font such as Courier // and in a display at least 120 columns wide. //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // program filedaemon // // purpose Output a specified file to an incoming connection to // a specified TCP port. // // This is something semi-useful to show how to make a simple // server using the daemon library. // // syntax filedaemon portnumber filename //----------------------------------------------------------------------------- #include #include #include #include #include #include #include #include #include //----------------------------------------------------------------------------- // Define macros to select which madvise appears to be available. // If none appears available, then just define them as zero. //----------------------------------------------------------------------------- #ifdef MADV_SEQUENTIAL #define madvise_sequential(p,s) madvise((p),(s),MADV_SEQUENTIAL) #else #ifdef POSIX_MADV_SEQUENTIAL #define madvise_sequential(p,s) posix_madvise((p),(s),POSIX_MADV_SEQUENTIAL) #else #define madvise_sequential(p,s) (0) #endif #endif #ifdef MADV_DONTNEED #define madvise_dontneed(p,s) madvise((p),(s),MADV_DONTNEED) #else #ifdef POSIX_MADV_DONTNEED #define madvise_dontneed(p,s) posix_madvise((p),(s),POSIX_MADV_DONTNEED) #else #define madvise_dontneed(p,s) (0) #endif #endif //----------------------------------------------------------------------------- // Main program. //----------------------------------------------------------------------------- int main ( int argc , char * * argv ) { static int num1 = 1; struct stat stat_buf ; struct sockaddr_in sock_addr ; socklen_t sock_addrlen ; struct servent * servent_p ; char * program_name ; char * file_name ; long start_count ; int master_pid ; int port_number ; int fd ; int fd_list [2] ; //-------------------------------------------------------- // At this point main() is running in the startup process. // This is the process that the shell is waiting to exit. //-------------------------------------------------------- program_name = argv[0]; daemon_set_program_name( program_name ); //------------------------------------ // Make sure we have enough arguments. //------------------------------------ if ( argc <= 2 ) { fprintf( stderr, "%s: need arguments: portnumber filename\n", program_name ); return 1; } //--------------------- // Get the port number. //--------------------- if ( '0' <= argv[1][0] && argv[1][0] <= '9' ) { port_number = strtoul( argv[1], NULL, 0 ); } else { servent_p = getservbyname( argv[1], "tcp" ); if ( ! servent_p ) { fprintf( stderr, "%s: unrecognized service port \"%s\"\n", program_name, argv[1] ); return 1; } port_number = servent_p->s_port; } if ( port_number > 65535 ) { fprintf( stderr, "%s: invalid port number \"%u\"\n", program_name, port_number ); return 1; } //---------------------------------------------------- // Get the file name. It does not have to exist, yet. //---------------------------------------------------- file_name = argv[2]; if ( stat( file_name, & stat_buf ) < 0 ) { fprintf( stderr, "WARNING: file \"%s\" does not exist, yet\n", file_name ); } else { if ( ! S_ISREG( stat_buf.st_mode ) ) { fprintf( stderr, "WARNING: file \"%s\" exists but is not a regular file\n", file_name ); } } //---------------------------------------------- // Run startup loop which starts master process. //---------------------------------------------- daemon_set_start_timeout( 30 ); if ( daemon_start() < 0 ) _exit( 1 ); //------------------------------- // Now running in master process. //------------------------------- argv[0][0] = 'M'; master_pid = getpid(); //-------------------------- // Set up the listen socket. //-------------------------- sock_addrlen = sizeof ( sock_addr ); sock_addr.sin_family = AF_INET; fd_list[0] = socket( PF_INET, SOCK_STREAM, IPPROTO_TCP ); if ( fd_list[0] < 0 ) { perror( "socket(PF_INET,SOCK_STREAM,IPPROTO_TCP)" ); _exit( 1 ); } if ( setsockopt( fd_list[0], SOL_SOCKET, SO_REUSEADDR, (void*) & num1, (socklen_t) sizeof ( num1 ) ) < 0 ) { perror( "setsockopt(,SOL_SOCKET,SO_REUSEADDR,,)" ); _exit( 1 ); } memset( & sock_addr, 0, sock_addrlen ); sock_addr.sin_port = htons( (u_int16_t) port_number ); sock_addr.sin_addr.s_addr = 0; if ( bind( fd_list[0], (struct sockaddr *) & sock_addr, (socklen_t) sock_addrlen ) < 0 ) { perror( "bind(fd_list[0],,)" ); _exit( 1 ); } if ( listen( fd_list[0], 128 ) < 0 ) { perror( "listen(,128)" ); _exit( 1 ); } fd_list[1] = -1; //------------------------------------------------------------ // Run master loop which starts and restarts server processes. //------------------------------------------------------------ daemon_set_restart_count_max( 256 ); if ( daemon_master( & start_count ) != 0 ) { _exit( 1 ); } //------------------------------- // Now running in server process. //------------------------------- argv[0][0] = 'S'; //----------------------------------------------------- // If this is the first server start, output a message. //----------------------------------------------------- if ( start_count == 0 ) { fprintf( stderr, "master PID = %u , server PID = %u\n", master_pid, getpid() ); daemon_success(); } //--------------------------------------------------- // Set maximum number of concurrent worker processes. //--------------------------------------------------- daemon_set_worker_count_max( 40 ); //------------------------------------------------------------------- // Run server loop which starts a worker process for each connection. //------------------------------------------------------------------- if ( daemon_server( fd_list, (struct sockaddr *) & sock_addr, & sock_addrlen ) < 0 ) _exit( 1 ); //------------------------------- // Now running in worker process. //------------------------------- argv[0][0] = 'W'; //--------------------------- // Access the specified file. //--------------------------- if ( stat( file_name, & stat_buf ) < 0 ) _exit( 1 ); if ( ! S_ISREG( stat_buf.st_mode ) ) _exit( 1 ); if ( ( fd = open( file_name, O_RDONLY ) ) < 0 ) _exit( 1 ); //--------------------- // Copy file to stdout. //--------------------- { off_t map_offset; map_offset = 0; while ( map_offset < stat_buf.st_size ) { void *map_ptr; size_t map_size = 65536; if ( map_offset + map_size > stat_buf.st_size ) map_size = stat_buf.st_size - map_offset; map_ptr = mmap( 0, map_size, PROT_READ, MAP_SHARED, fd, map_offset ); if ( map_ptr == MAP_FAILED ) _exit( 1 ); madvise_sequential( map_ptr, map_size ); { void *write_ptr = map_ptr; size_t write_size = map_size; while ( write_size ) { ssize_t write_len; if ( ( write_len = write( 1, write_ptr, write_size ) ) < 0 ) _exit( 1 ); write_ptr += write_len; write_size -= write_len; } } madvise_dontneed( map_ptr, map_size ); if ( munmap( map_ptr, map_size ) < 0 ) _exit( 1 ); map_offset += map_size; } } //---------------- // Close and exit. //---------------- shutdown( 1, SHUT_WR ); close( fd ); _exit( 0 ); }