2019-06-01 11:08:55 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2017-10-05 23:46:54 +03:00
/* Control socket for client/server test execution
*
* Copyright ( C ) 2017 Red Hat , Inc .
*
* Author : Stefan Hajnoczi < stefanha @ redhat . com >
*/
/* The client and server may need to coordinate to avoid race conditions like
* the client attempting to connect to a socket that the server is not
* listening on yet . The control socket offers a communications channel for
* such coordination tasks .
*
* If the client calls control_expectln ( " LISTENING " ) , then it will block until
* the server calls control_writeln ( " LISTENING " ) . This provides a simple
* mechanism for coordinating between the client and the server .
*/
# include <errno.h>
# include <netdb.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <unistd.h>
# include <sys/types.h>
# include <sys/socket.h>
# include "timeout.h"
# include "control.h"
static int control_fd = - 1 ;
/* Open the control socket, either in server or client mode */
void control_init ( const char * control_host ,
const char * control_port ,
bool server )
{
struct addrinfo hints = {
. ai_socktype = SOCK_STREAM ,
} ;
struct addrinfo * result = NULL ;
struct addrinfo * ai ;
int ret ;
ret = getaddrinfo ( control_host , control_port , & hints , & result ) ;
if ( ret ! = 0 ) {
fprintf ( stderr , " %s \n " , gai_strerror ( ret ) ) ;
exit ( EXIT_FAILURE ) ;
}
for ( ai = result ; ai ; ai = ai - > ai_next ) {
int fd ;
int val = 1 ;
fd = socket ( ai - > ai_family , ai - > ai_socktype , ai - > ai_protocol ) ;
if ( fd < 0 )
continue ;
if ( ! server ) {
if ( connect ( fd , ai - > ai_addr , ai - > ai_addrlen ) < 0 )
goto next ;
control_fd = fd ;
printf ( " Control socket connected to %s:%s. \n " ,
control_host , control_port ) ;
break ;
}
if ( setsockopt ( fd , SOL_SOCKET , SO_REUSEADDR ,
& val , sizeof ( val ) ) < 0 ) {
perror ( " setsockopt " ) ;
exit ( EXIT_FAILURE ) ;
}
if ( bind ( fd , ai - > ai_addr , ai - > ai_addrlen ) < 0 )
goto next ;
if ( listen ( fd , 1 ) < 0 )
goto next ;
printf ( " Control socket listening on %s:%s \n " ,
control_host , control_port ) ;
fflush ( stdout ) ;
control_fd = accept ( fd , NULL , 0 ) ;
close ( fd ) ;
if ( control_fd < 0 ) {
perror ( " accept " ) ;
exit ( EXIT_FAILURE ) ;
}
printf ( " Control socket connection accepted... \n " ) ;
break ;
next :
close ( fd ) ;
}
if ( control_fd < 0 ) {
fprintf ( stderr , " Control socket initialization failed. Invalid address %s:%s? \n " ,
control_host , control_port ) ;
exit ( EXIT_FAILURE ) ;
}
freeaddrinfo ( result ) ;
}
/* Free resources */
void control_cleanup ( void )
{
close ( control_fd ) ;
control_fd = - 1 ;
}
/* Write a line to the control socket */
void control_writeln ( const char * str )
{
ssize_t len = strlen ( str ) ;
ssize_t ret ;
timeout_begin ( TIMEOUT ) ;
do {
ret = send ( control_fd , str , len , MSG_MORE ) ;
timeout_check ( " send " ) ;
} while ( ret < 0 & & errno = = EINTR ) ;
if ( ret ! = len ) {
perror ( " send " ) ;
exit ( EXIT_FAILURE ) ;
}
do {
ret = send ( control_fd , " \n " , 1 , 0 ) ;
timeout_check ( " send " ) ;
} while ( ret < 0 & & errno = = EINTR ) ;
if ( ret ! = 1 ) {
perror ( " send " ) ;
exit ( EXIT_FAILURE ) ;
}
timeout_end ( ) ;
}
2023-01-10 13:15:15 +03:00
void control_writeulong ( unsigned long value )
{
char str [ 32 ] ;
if ( snprintf ( str , sizeof ( str ) , " %lu " , value ) > = sizeof ( str ) ) {
perror ( " snprintf " ) ;
exit ( EXIT_FAILURE ) ;
}
control_writeln ( str ) ;
}
unsigned long control_readulong ( void )
{
unsigned long value ;
char * str ;
str = control_readln ( ) ;
if ( ! str )
exit ( EXIT_FAILURE ) ;
value = strtoul ( str , NULL , 10 ) ;
free ( str ) ;
return value ;
}
2017-10-05 23:46:54 +03:00
/* Return the next line from the control socket (without the trailing newline).
*
* The program terminates if a timeout occurs .
*
* The caller must free ( ) the returned string .
*/
char * control_readln ( void )
{
char * buf = NULL ;
size_t idx = 0 ;
size_t buflen = 0 ;
timeout_begin ( TIMEOUT ) ;
for ( ; ; ) {
ssize_t ret ;
if ( idx > = buflen ) {
char * new_buf ;
new_buf = realloc ( buf , buflen + 80 ) ;
if ( ! new_buf ) {
perror ( " realloc " ) ;
exit ( EXIT_FAILURE ) ;
}
buf = new_buf ;
buflen + = 80 ;
}
do {
ret = recv ( control_fd , & buf [ idx ] , 1 , 0 ) ;
timeout_check ( " recv " ) ;
} while ( ret < 0 & & errno = = EINTR ) ;
if ( ret = = 0 ) {
fprintf ( stderr , " unexpected EOF on control socket \n " ) ;
exit ( EXIT_FAILURE ) ;
}
if ( ret ! = 1 ) {
perror ( " recv " ) ;
exit ( EXIT_FAILURE ) ;
}
if ( buf [ idx ] = = ' \n ' ) {
buf [ idx ] = ' \0 ' ;
break ;
}
idx + + ;
}
timeout_end ( ) ;
return buf ;
}
/* Wait until a given line is received or a timeout occurs */
void control_expectln ( const char * str )
{
char * line ;
line = control_readln ( ) ;
2019-12-18 21:07:06 +03:00
control_cmpln ( line , str , true ) ;
free ( line ) ;
}
bool control_cmpln ( char * line , const char * str , bool fail )
{
if ( strcmp ( str , line ) = = 0 )
return true ;
if ( fail ) {
2017-10-05 23:46:54 +03:00
fprintf ( stderr , " expected \" %s \" on control socket, got \" %s \" \n " ,
str , line ) ;
exit ( EXIT_FAILURE ) ;
}
2019-12-18 21:07:06 +03:00
return false ;
2017-10-05 23:46:54 +03:00
}