Subversion Repositories HelenOS

Rev

Rev 4699 | Rev 4708 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Copyright (c) 2009 Lukas Mejdrech
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  *
  9.  * - Redistributions of source code must retain the above copyright
  10.  *   notice, this list of conditions and the following disclaimer.
  11.  * - Redistributions in binary form must reproduce the above copyright
  12.  *   notice, this list of conditions and the following disclaimer in the
  13.  *   documentation and/or other materials provided with the distribution.
  14.  * - The name of the author may not be used to endorse or promote products
  15.  *   derived from this software without specific prior written permission.
  16.  *
  17.  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  18.  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  19.  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  20.  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  21.  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  22.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  23.  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  24.  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  26.  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27.  */
  28.  
  29. /** @addtogroup echo
  30.  *  @{
  31.  */
  32.  
  33. /** @file
  34.  */
  35.  
  36. #include <malloc.h>
  37. #include <stdio.h>
  38. #include <string.h>
  39. #include <task.h>
  40.  
  41. #include "../../include/in.h"
  42. #include "../../include/inet.h"
  43. #include "../../include/socket.h"
  44.  
  45. #include "../../err.h"
  46.  
  47. /** Echo module name.
  48.  */
  49. #define NAME    "Echo"
  50.  
  51. /** Module entry point.
  52.  *  Reads command line parameters and starts listenning.
  53.  *  @param argc The number of command line parameters. Input parameter.
  54.  *  @param argv The command line parameters. Input parameter.
  55.  *  @returns EOK on success.
  56.  */
  57. int     main( int argc, char * argv[] );
  58.  
  59. /** Prints the application help.
  60.  */
  61. void    print_help( void );
  62.  
  63. /** Prints the parameter unrecognized message and the application help.
  64.  *  @param index The index of the parameter. Input parameter.
  65.  *  @param parameter The parameter name. Input parameter.
  66.  */
  67. void    print_unrecognized( int index, const char * parameter );
  68.  
  69. /** Parses the next parameter as an integral number.
  70.  *  Uses the offseted actual parameter if the offset is set or the next one if not.
  71.  *  @param argc The total number of the parameters. Input parameter.
  72.  *  @param argv The parameters. Input parameter.
  73.  *  @param index The actual parameter index. Input/output parameter.
  74.  *  @param value The parsed parameter value. Output parameter.
  75.  *  @param name The parameter name to be printed on errors. Input parameter.
  76.  *  @param offset The value offset in the actual parameter. If not set, the next parameter is parsed instead. Input parameter.
  77.  *  @returns EOK on success.
  78.  *  @returns EINVAL if the parameter is missing.
  79.  *  @returns EINVAL if the parameter is in wrong format.
  80.  */
  81. int parse_parameter_int( int argc, char ** argv, int * index, int * value, const char * name, int offset );
  82.  
  83. /** Parses the next parameter as a character string.
  84.  *  Uses the offseted actual parameter if the offset is set or the next one if not.
  85.  *  @param argc The total number of the parameters. Input parameter.
  86.  *  @param argv The parameters. Input parameter.
  87.  *  @param index The actual parameter index. Input/output parameter.
  88.  *  @param value The parsed parameter value. Output parameter.
  89.  *  @param name The parameter name to be printed on errors. Input parameter.
  90.  *  @param offset The value offset in the actual parameter. If not set, the next parameter is parsed instead. Input parameter.
  91.  *  @returns EOK on success.
  92.  *  @returns EINVAL if the parameter is missing.
  93.  */
  94. int parse_parameter_string( int argc, char ** argv, int * index, char ** value, const char * name, int offset );
  95.  
  96. /** Parses the next named parameter as an integral number.
  97.  *  Uses the offseted actual parameter if the offset is set or the next one if not.
  98.  *  Translates the parameter using the parse_value function.
  99.  *  @param argc The total number of the parameters. Input parameter.
  100.  *  @param argv The parameters. Input parameter.
  101.  *  @param index The actual parameter index. Input/output parameter.
  102.  *  @param value The parsed parameter value. Output parameter.
  103.  *  @param name The parameter name to be printed on errors. Input parameter.
  104.  *  @param offset The value offset in the actual parameter. If not set, the next parameter is parsed instead. Input parameter.
  105.  *  @param parse_value The translation function to parse the named value.
  106.  *  @returns EOK on success.
  107.  *  @returns EINVAL if the parameter is missing.
  108.  *  @returns ENOENT if the parameter name has not been found.
  109.  */
  110. int parse_parameter_name_int( int argc, char ** argv, int * index, int * value, const char * name, int offset, int ( * parse_value )( const char * value ));
  111.  
  112. /** Translates the character string to the protocol family number.
  113.  *  @param name The protocol family name. Input parameter.
  114.  *  @returns The corresponding protocol family number.
  115.  */
  116. int parse_protocol_family( const char * name );
  117.  
  118. /** Translates the character string to the socket type number.
  119.  *  @param name The socket type name. Input parameter.
  120.  *  @returns The corresponding socket type number.
  121.  */
  122. int parse_socket_type( const char * name );
  123.  
  124. void print_help( void ){
  125.     printf(
  126.         "Network Echo aplication\n" \
  127.         "Usage: echo [options]\n" \
  128.         "Where options are:\n" \
  129.         "-p port_number | --port=port_number\n" \
  130.         "\tThe port number the application should listen at. The default is 7.\n" \
  131.         "\n" \
  132.         "-s receive_size | --size=receive_size\n" \
  133.         "\tThe maximum receive data size the application should accept. The default is 1024 bytes.\n" \
  134.         "\n" \
  135.         "-c count | --count\n" \
  136.         "\tThe number of received messages to handle. A negative number means infinity. The default is infinity.\n" \
  137.         "\n" \
  138.         "-r reply_string | --reply=reply_string\n" \
  139.         "\tThe constant reply string. The default is the original data received.\n" \
  140.         "\n" \
  141.         "-f protocol_family | --family=protocol_family\n" \
  142.         "\tThe listenning socket protocol family. Only the PF_INET is supported.\n"
  143.         "\n" \
  144.         "-h | --help\n" \
  145.         "\tShow this application help.\n"
  146.         "\n" \
  147.         "-t socket_type | --type=socket_type\n" \
  148.         "\tThe listenning socket type. Only the SOCK_DGRAM is supported.\n" \
  149.         "\n" \
  150.         "-v | --verbose\n" \
  151.         "\tShow all output messages.\n"
  152.     );
  153. }
  154.  
  155. int parse_parameter_int( int argc, char ** argv, int * index, int * value, const char * name, int offset ){
  156.     char *  rest;
  157.  
  158.     if( offset ){
  159.         * value = strtol( argv[ * index ] + offset, & rest, 10 );
  160.     }else if(( * index ) + 1 < argc ){
  161.         ++ ( * index );
  162.         * value = strtol( argv[ * index ], & rest, 10 );
  163.     }else{
  164.         fprintf( stderr, "Command line error: missing %s\n", name );
  165.         return EINVAL;
  166.     }
  167.     if( rest && ( * rest )){
  168.         fprintf( stderr, "Command line error: %s unrecognized (%d: %s)\n", name, * index, argv[ * index ] );
  169.         return EINVAL;
  170.     }
  171.     return EOK;
  172. }
  173.  
  174. int parse_parameter_string( int argc, char ** argv, int * index, char ** value, const char * name, int offset ){
  175.     if( offset ){
  176.         * value = argv[ * index ] + offset;
  177.     }else if(( * index ) + 1 < argc ){
  178.         ++ ( * index );
  179.         * value = argv[ * index ];
  180.     }else{
  181.         fprintf( stderr, "Command line error: missing %s\n", name );
  182.         return EINVAL;
  183.     }
  184.     return EOK;
  185. }
  186.  
  187. int parse_parameter_name_int( int argc, char ** argv, int * index, int * value, const char * name, int offset, int ( * parse_value )( const char * value )){
  188.     ERROR_DECLARE;
  189.  
  190.     char *  parameter;
  191.  
  192.     ERROR_PROPAGATE( parse_parameter_string( argc, argv, index, & parameter, name, offset ));
  193.     * value = ( * parse_value )( parameter );
  194.     if(( * value ) == ENOENT ){
  195.         fprintf( stderr, "Command line error: unrecognized %s value (%d: %s)\n", name, * index, parameter );
  196.         return ENOENT;
  197.     }
  198.     return EOK;
  199. }
  200.  
  201. int parse_protocol_family( const char * name ){
  202.     if( str_lcmp( name, "PF_INET", 7 ) == 0 ){
  203.         return PF_INET;
  204.     }
  205.     return ENOENT;
  206. }
  207.  
  208. int parse_socket_type( const char * name ){
  209.     if( str_lcmp( name, "SOCK_DGRAM", 11 ) == 0 ){
  210.         return SOCK_DGRAM;
  211.     }
  212.     return ENOENT;
  213. }
  214.  
  215. void print_unrecognized( int index, const char * parameter ){
  216.     fprintf( stderr, "Command line error - unrecognized parameter (%d: %s)\n", index, parameter );
  217.     print_help();
  218. }
  219.  
  220. int main( int argc, char * argv[] ){
  221.     ERROR_DECLARE;
  222.  
  223.     int                 size            = 1024;
  224.     int                 verbose         = 0;
  225.     char *              reply           = NULL;
  226.     sock_type_t         type            = SOCK_DGRAM;
  227.     int                 count           = -1;
  228.     struct sockaddr_in  address         = { .sin_family = PF_INET, .sin_port = 7 };
  229.  
  230.     int                 socket_id;
  231.     int                 address_length;
  232.     char                address_string[ INET_ADDRSTRLEN ];
  233.     char *              data;
  234.     int                 length;
  235.     int                 index;
  236.     size_t              reply_length;
  237.     int                 value;
  238.  
  239.     printf( "Task %d - ", task_get_id());
  240.     printf( "%s\n", NAME );
  241.  
  242.     for( index = 1; index < argc; ++ index ){
  243.         if( argv[ index ][ 0 ] == '-' ){
  244.             switch( argv[ index ][ 1 ] ){
  245.                 case 'c':   ERROR_PROPAGATE( parse_parameter_int( argc, argv, & index, & count, "count", 0 ));
  246.                             break;
  247.                 case 'f':   ERROR_PROPAGATE( parse_parameter_name_int( argc, argv, & index, & value, "protocol family", 0, parse_protocol_family ));
  248.                             address.sin_family = ( uint16_t ) value;
  249.                             break;
  250.                 case 'h':   print_help();
  251.                             return EOK;
  252.                             break;
  253.                 case 'p':   ERROR_PROPAGATE( parse_parameter_int( argc, argv, & index, & value, "port number", 0 ));
  254.                             address.sin_port = value;
  255.                             break;
  256.                 case 'r':   ERROR_PROPAGATE( parse_parameter_string( argc, argv, & index, & reply, "reply string", 0 ));
  257.                             break;
  258.                 case 's':   ERROR_PROPAGATE( parse_parameter_int( argc, argv, & index, & size, "receive size", 0 ));
  259.                             break;
  260.                 case 't':   ERROR_PROPAGATE( parse_parameter_name_int( argc, argv, & index, ( int * ) & type, "socket_type", 0, parse_socket_type ));
  261.                             break;
  262.                 case 'v':   verbose = 1;
  263.                             break;
  264.                 case '-':   if( str_lcmp( argv[ index ] + 2, "count=", 6 ) == 0 ){
  265.                                 ERROR_PROPAGATE( parse_parameter_int( argc, argv, & index, & count, "received count", 8 ))
  266.                             }else if( str_lcmp( argv[ index ] + 2, "family=", 7 ) == 0 ){
  267.                                 ERROR_PROPAGATE( parse_parameter_name_int( argc, argv, & index, & value, "protocol family", 9, parse_protocol_family ));
  268.                                 address.sin_family = value;
  269.                             }else if( str_lcmp( argv[ index ] + 2, "help", 5 ) == 0 ){
  270.                                 print_help();
  271.                                 return EOK;
  272.                             }else if( str_lcmp( argv[ index ] + 2, "port=", 5 ) == 0 ){
  273.                                 ERROR_PROPAGATE( parse_parameter_int( argc, argv, & index, & value, "port number", 7 ));
  274.                                 address.sin_port = value;
  275.                             }else if( str_lcmp( argv[ index ] + 2, "reply=", 6 ) == 0 ){
  276.                                 ERROR_PROPAGATE( parse_parameter_string( argc, argv, & index, & reply, "reply string", 8 ));
  277.                             }else if( str_lcmp( argv[ index ] + 2, "size=", 5 ) == 0 ){
  278.                                 ERROR_PROPAGATE( parse_parameter_int( argc, argv, & index, & size, "receive size", 7 ));
  279.                             }else if( str_lcmp( argv[ index ] + 2, "type=", 5 ) == 0 ){
  280.                                 ERROR_PROPAGATE( parse_parameter_name_int( argc, argv, & index, ( int * ) & type, "socket_type", 7, parse_socket_type ));
  281.                             }else if( str_lcmp( argv[ index ] + 2, "verbose", 8 ) == 0 ){
  282.                                 verbose = 1;
  283.                             }else{
  284.                                 print_unrecognized( index, argv[ index ] + 2 );
  285.                                 return EINVAL;
  286.                             }
  287.                             break;
  288.                 default:
  289.                     print_unrecognized( index, argv[ index ] + 1 );
  290.                     return EINVAL;
  291.             }
  292.         }else{
  293.             print_unrecognized( index, argv[ index ] );
  294.             return EINVAL;
  295.         }
  296.     }
  297.  
  298.     if( size <= 0 ){
  299.         fprintf( stderr, "Receive size too small (%d). Using 1024 bytes instead.\n", size );
  300.         size = 1024;
  301.     }
  302.     data = ( char * ) malloc( size + 1 );
  303.     if( ! data ){
  304.         fprintf( stderr, "Failed to allocate receive buffer.\n" );
  305.         return ENOMEM;
  306.     }
  307.  
  308.     reply_length = reply ? str_length( reply ) : 0;
  309.  
  310.     socket_id = socket( address.sin_family, type, 0 );
  311.     if( socket_id < 0 ){
  312.         fprintf( stderr, "Socket create error %d\n", socket_id );
  313.         return socket_id;
  314.     }
  315.     if( ERROR_OCCURRED( bind( socket_id, ( struct sockaddr * ) & address, sizeof( address )))){
  316.         fprintf( stderr, "Socket bind error %d\n", ERROR_CODE );
  317.         return ERROR_CODE;
  318.     }
  319.  
  320.     if( verbose ) printf( "Listenning at %d\n", address.sin_port );
  321.  
  322.     while( count ){
  323.         address_length = sizeof( address );
  324.         length = recvfrom( socket_id, data, size, 0, ( struct sockaddr * ) & address, & address_length );
  325.         if( length < 0 ){
  326.             fprintf( stderr, "Socket receive error %d\n", length );
  327.         }else{
  328.             if( verbose ){
  329.                 if( ERROR_OCCURRED( inet_ntop( address.sin_family, ( uint8_t * ) & address.sin_addr.s_addr, address_string, sizeof( address_string )))){
  330.                     fprintf( stderr, "Received address error %d\n", ERROR_CODE );
  331.                     continue;
  332.                 }else{
  333.                     data[ length ] = '\0';
  334.                     printf( "Received from %s:%d\n%s\n", address_string, address.sin_port, data );
  335.                 }
  336.             }
  337.             if( ERROR_OCCURRED( sendto( socket_id, reply ? reply : data, reply ? reply_length : ( size_t ) length, 0, ( struct sockaddr * ) & address, sizeof( address )))){
  338.                 fprintf( stderr, "Socket send error %d\n", ERROR_CODE );
  339.             }
  340.         }
  341.         if( count > 0 ){
  342.             -- count;
  343.             if( verbose ) printf( "Waiting for next %d packet(s)\n", count );
  344.         }
  345.     }
  346.  
  347.     if( verbose ) printf( "Closing the socket\n" );
  348.  
  349.     if( ERROR_OCCURRED( closesocket( socket_id ))){
  350.         fprintf( stderr, "Close socket error %d\n", ERROR_CODE );
  351.         return ERROR_CODE;
  352.     }
  353.  
  354.     if( verbose ) printf( "Exiting\n" );
  355.  
  356.     return EOK;
  357. }
  358.  
  359. /** @}
  360.  */
  361.