// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// File        : udp_console_u.c
// Description : Linux console based on UDP protocol
// License     :
//
//  Copyright (C) 2011 Rachid Koucha <rachid dot koucha at free dot fr>
//
//
//  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 3 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, see <http://www.gnu.org/licenses/>.
//
//
// Evolutions  :
//
//     19-Jan-2011  R. Koucha    - Creation
//
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=


#include <getopt.h>
#include <errno.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netdb.h>
#include <libgen.h>
#include <string.h>
#include <arpa/inet.h>

#include "config.h"


// ----------------------------------------------------------------------------
// Name   : udpc_version
// Usage  : Version of the software
// ----------------------------------------------------------------------------
static char *udpc_version = UDPC_VERSION;



// ----------------------------------------------------------------------------
// Name        : UDPC_ERR
// Description : Display of an error message
// ----------------------------------------------------------------------------
#define UDPC_ERR(format, ...)                                             \
           do {                                                           \
	     fprintf(stderr, "UDPC(%s#%d): " format, __FUNCTION__, __LINE__, ## __VA_ARGS__); \
              } while (0)


// ----------------------------------------------------------------------------
// Name        : udpc_buf
// Description : Reception buffer
// Note        : The size of the console messages (i.e. from printk/vprintk)
//               are 1024 + timestamp + weight = 1024 + 17 = 1041 bytes long
// ----------------------------------------------------------------------------
static char udpc_buf[1152];


// ----------------------------------------------------------------------------
// Name        : UDPC_DEFAULT_PORT
// Description : Default UDP port
// ----------------------------------------------------------------------------
#define UDPC_DEFAULT_PORT 23


// ----------------------------------------------------------------------------
// Name        : udpc_sd
// Description : Socket on the network
// ----------------------------------------------------------------------------
static int udpc_sd;



// ----------------------------------------------------------------------------
// Name        : udpc_port
// Description : Port of remote server
// ----------------------------------------------------------------------------
static in_port_t udpc_port;







// ----------------------------------------------------------------------------
// Name   : udpc_longops
// Usage  : Option on the command line
// ----------------------------------------------------------------------------
static struct option udpc_longopts[] =
{
  { "port",       required_argument, NULL, 'p' },
  { "version",    no_argument,       NULL, 'V' },
  { "help",       no_argument,       NULL, 'h' },

  // Last entry
  {NULL, 0, NULL, 0 }
};


//---------------------------------------------------------------------------
// Name : udpc_help
// Usage: Display help
//----------------------------------------------------------------------------
static void udpc_help(char *prog)
{
char *p = basename(prog);

fprintf(stderr,
        "\n%s %s\n"
        "\n"
        "Copyright (C) 2011  Rachid Koucha\n"
        "\n"
        "This program comes with ABSOLUTELY NO WARRANTY.\n"
        "This is free software, and you are welcome to redistribute it\n"
        "under the terms of the GNU General Public License as published by\n"
        "the Free Software Foundation; either version 3 of the License , or\n"
        "(at your option) any later version.\n"
        "\n"
        "Usage: %s [<options> ...]\n"
        "\n"
        "Linux console based on UDP protocol : it captures and displays\n"
        "the messages generated by the kernel.\n"
        "\n"
        "Options:\n"
        "\n"
        "\t-p | --port              : UDP port from which messages are received (default: %u)\n"
        "\t-V | --version           : Display the version\n"
        "\t-h | --help              : This help\n"
        ,
        p, udpc_version,
        p,
        UDPC_DEFAULT_PORT
	);
} // udpc_help



// ----------------------------------------------------------------------------
// Name        : main
// Description : Program's entry point
// Return      : 0, if OK
//               1, if error
// ----------------------------------------------------------------------------
int main(int ac, char *av[])
{
ssize_t             sz;
struct sockaddr_in  from;
socklen_t           fromlen;
int                 rc;
struct sockaddr_in  local_addr; 
int                 opt;


  // Default initializations
  udpc_port = UDPC_DEFAULT_PORT;

  while ((opt = getopt_long(ac, av, "p:Vh", udpc_longopts, NULL)) != EOF)
  {
    switch(opt)
    {
      case 'p' : // Port
      {
        udpc_port = atoi(optarg);
      }
      break;

      case 'V' : // Version
      {
        printf("UDPC version: %s\n", udpc_version);
        return 0;
      }
      break;

      case 'h' : // Help
      {
        udpc_help(av[0]);
        return 0;
      }
      break;

      case '?' :
      default :
      {
        udpc_help(av[0]);
        return 1;
      }
    } // End switch
  } // End while

  // Create the socket
  udpc_sd = socket(PF_INET, SOCK_DGRAM, 0);
  if (udpc_sd < 0)
  {
    UDPC_ERR("Error %d on socket()\n", errno);
    return 1;
  }

  // Make the local address
  memset(&local_addr, 0, sizeof(local_addr));
  local_addr.sin_family      = AF_INET;
  local_addr.sin_port        = htons(udpc_port);
  local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  
  rc = bind(udpc_sd, (struct sockaddr *)&local_addr, sizeof(local_addr));
  if (rc < 0)
  {
    UDPC_ERR("Error %d on socket()\n", errno);
    return 1;
  }

  printf("Listening on UDP port %u\n", udpc_port);

  // For ever...
  while(1)
  {
    // Reception of a message
    fromlen = sizeof(from);
    sz = recvfrom(udpc_sd,
                  udpc_buf, sizeof(udpc_buf) - 1,
                  0,
                  (struct sockaddr *)&from, &fromlen);

    // In case of error we go out...
    if (sz < 0)
    {
      UDPC_ERR("Error %d on recvfrom()\n", errno);
      return 1;
    }

    // Display the message on stdout
    if (sz > 0)
    {
      udpc_buf[sz] = '\0';
      printf("%s", udpc_buf);
      fflush(stdout);
    }
  } // End while

  return 0;
} // main

