//-----------------------------------------------------------------------------
// 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	httpaddr
//
// purpose	Show an HTTP client its IP address and port number in HTML.
//		The output is always HTML regardless of the request.
//
//		This is something semi-useful to show how to make a simple
//		server using the daemon library.
//
// syntax	httpaddr portnum [ portnum ... ]
//
// portnum	One or more port numbers to listen to.
//
// bugs		This code only pretends to do HTTP.
//-----------------------------------------------------------------------------
#include <alloca.h>
#include <arpa/inet.h>
#include <errno.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <time.h>
#include <unistd.h>

#include <libh/daemon.h>

int main ( int argc, char **argv )
{
    static int		num1		= 1;
    struct sockaddr_in	sock_addr	;
    socklen_t		sock_addrlen	;
    int *		desc_list	;
    int *		port_list	;
    time_t		now		;
    size_t		len		;
    long		s		;
    int			n		;
    int			port_count	;
    int			master_pid	;
    char		date_str	[32];
    char		addr_str	[64];
    char		buffer		[1536];

    if ( argc <= 1 ) return 1;

    //--------------------------------
    // Now running in startup process.
    //--------------------------------
    daemon_set_program_name( argv[0] );
    if ( ! ( desc_list = alloca( sizeof (int) * argc ) ) ) return 1;
    if ( ! ( port_list = alloca( sizeof (int) * argc ) ) ) return 1;
    port_count = argc - 1;
    for ( n = 0; n < port_count; ++ n ) {
	port_list[n] = strtoul( argv[n+1], NULL, 0 );
    }
    port_list[port_count] = -1;

    //----------------------------------------------
    // Run startup loop which starts master process.
    //----------------------------------------------
    daemon_set_start_timeout( 30 );
    if ( ( n = daemon_start() ) < 0 ) _exit( 1 );

    //-------------------------------
    // Now running in master process.
    //-------------------------------
    argv[0][0] = 'M';
    master_pid = getpid();

    //-----------------------------
    // Set up sockets to listen on.
    //-----------------------------
    sock_addrlen = sizeof( sock_addr );
    sock_addr.sin_family = AF_INET;
    for ( n = 0; n < port_count; ++ n ) {
	if ( ( desc_list[n] = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP) ) < 0 )
	    _exit( ( perror( "socket(PF_INET,SOCK_STREAM,IPPROTO_TCP)" ), 1 ) );
	if ( setsockopt(desc_list[n],SOL_SOCKET,SO_REUSEADDR,(void*)&num1,(socklen_t)sizeof(num1)) < 0 )
	    _exit( ( perror( "setsockopt(,SOL_SOCKET,SO_REUSEADDR,,)" ), 1 ) );
	memset( & sock_addr, 0, sock_addrlen );
	sock_addr.sin_port = htons( (u_int16_t) port_list[n] );
	sock_addr.sin_addr.s_addr = 0;
	if ( bind(desc_list[n],(struct sockaddr*)&sock_addr,(socklen_t)sock_addrlen) < 0 )
	    _exit( ( perror( "bind()" ), 1 ) );
	if ( listen( desc_list[n], 128 ) < 0 )
	    _exit( ( perror( "listen(,128)" ), 1 ) );
    }
    desc_list[port_count] = -1;

    //------------------------------------------------------------
    // Run master loop which starts and restarts server processes.
    //------------------------------------------------------------
    daemon_set_restart_count_max( 256 );
    if ( daemon_master( & s ) != 0 ) _exit( 1 );

    //-------------------------------
    // Now running in server process.
    //-------------------------------
    argv[0][0] = 'S';
    if ( s == 0 ) {
	fprintf( stderr, "master PID = %u , server PID = %u\n", master_pid, getpid() );
	daemon_success();
    }
    daemon_set_server_tick( 20, 0 );
    daemon_set_worker_count_max( 40 );

    //-------------------------------------------------------------------
    // Run server loop which starts a worker process for each connection.
    //-------------------------------------------------------------------
    if ( daemon_server( desc_list, (struct sockaddr *) & sock_addr, & sock_addrlen ) < 0 ) _exit( 1 );

    //-------------------------------
    // Now running in worker process.
    //-------------------------------
    argv[0][0] = 'W';
    inet_ntop( sock_addr.sin_family, & sock_addr.sin_addr, addr_str, sizeof (addr_str) );
    now = time( NULL );
    strftime( date_str, sizeof (date_str), "%a, %m %b %Y %T", gmtime( & now ) );
    len = snprintf( buffer, sizeof (buffer),
		    "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n"
		    "<html>\n"
		    "<head>\n"
		    "<title>Client address %s - Client port %u</title>\n"
		    "</head>\n"
		    "<meta http-equiv=\"Cache-Control\" content=\"no-cache\"/>\n"
		    "<meta http-equiv=\"Pragma\" content=\"no-cache\"/>\n"
		    "<meta http-equiv=\"Connection\" content=\"close\"/>\n"
		    "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\"/>\n"
		    "<body bgcolor=\"#ffffff\" text=\"#000000\">\n"
		    "<font size=+2><b>Date: <serverdatetime>%s GMT</serverdatetime><br>\n"
		    "Client IP address: <clientaddress>%s</clientaddress> [%08lx] (%lu)<br>\n"
		    "Client port number: <clientport>%u</clientport> [%04x]</b></font>\n"
		    "</body>\n"
		    "</html>\n",
		    addr_str,
		    (unsigned int) ntohs( sock_addr.sin_port ),
		    date_str,
		    addr_str,
		    (unsigned long) ntohl( sock_addr.sin_addr.s_addr ),
		    (unsigned long) ntohl( sock_addr.sin_addr.s_addr ),
		    (unsigned int) ntohs( sock_addr.sin_port ),
		    (unsigned int) ntohs( sock_addr.sin_port )
	);
    printf( "HTTP/1.1 200 OK\r\n"
	    "Date: %s GMT\r\n"
	    "Expires: %s GMT\r\n"
	    "Last-Modified: %s GMT\r\n"
	    "Server: httpaddr libh/daemon\r\n"
	    "Cache-Control: private\r\n"
	    "Cache-Control: no-transform\r\n"
	    "Cache-Control: no-cache\r\n"
	    "Pragma: no-cache\r\n"
	    "Connection: close\r\n"
	    "Content-Type: text/html\r\n"
	    "Content-Length: %u\r\n"
	    "X-Client-Address: %s\r\n"
	    "X-Client-Port: %u\r\n"
	    "\r\n",
	    date_str,
	    date_str,
	    date_str,
	    (unsigned int) len,
	    addr_str,
	    (unsigned int) ntohs( sock_addr.sin_port )
	);
    fputs( buffer, stdout );
    fflush( stdout );
    shutdown( 1, SHUT_WR );
    fclose( stdout );
    _exit( 0 );
}

