2010-05-25 15:14:06 +04:00
/*
* commandtest . c : Test the libCommand API
*
2014-02-19 05:06:50 +04:00
* Copyright ( C ) 2010 - 2014 Red Hat , Inc .
2010-05-25 15:14:06 +04:00
*
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ; either
* version 2.1 of the License , or ( at your option ) any later version .
*
* This library 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
* Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public
2012-09-21 02:30:55 +04:00
* License along with this library . If not , see
2012-07-21 14:06:23 +04:00
* < http : //www.gnu.org/licenses/>.
2010-05-25 15:14:06 +04:00
*/
# include <config.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <unistd.h>
# include <signal.h>
# include <sys/stat.h>
2014-03-04 11:39:56 +04:00
# include <sys/wait.h>
2010-05-25 15:14:06 +04:00
# include <fcntl.h>
# include "testutils.h"
# include "internal.h"
# include "nodeinfo.h"
2012-12-12 22:06:53 +04:00
# include "viralloc.h"
2012-12-12 20:27:01 +04:00
# include "vircommand.h"
2011-07-19 22:32:58 +04:00
# include "virfile.h"
2011-08-13 01:54:49 +04:00
# include "virpidfile.h"
2012-12-13 22:21:53 +04:00
# include "virerror.h"
2013-01-16 14:58:00 +04:00
# include "virthread.h"
2013-04-03 14:36:23 +04:00
# include "virstring.h"
2014-02-20 07:04:40 +04:00
# include "virprocess.h"
2012-06-01 01:50:07 +04:00
# define VIR_FROM_THIS VIR_FROM_NONE
2010-05-25 15:14:06 +04:00
2013-01-16 14:58:00 +04:00
typedef struct _virCommandTestData virCommandTestData ;
typedef virCommandTestData * virCommandTestDataPtr ;
struct _virCommandTestData {
virMutex lock ;
virThread thread ;
bool quit ;
bool running ;
} ;
2010-05-25 15:14:06 +04:00
# ifdef WIN32
2011-07-28 19:48:12 +04:00
int
main ( void )
2010-05-25 15:14:06 +04:00
{
2011-07-28 19:48:12 +04:00
return EXIT_AM_SKIP ;
2010-05-25 15:14:06 +04:00
}
# else
2014-07-16 18:19:28 +04:00
static int checkoutput ( const char * testname ,
char * prefix )
2010-12-06 13:51:41 +03:00
{
2010-05-25 15:14:06 +04:00
int ret = - 1 ;
char * expectname = NULL ;
char * expectlog = NULL ;
char * actualname = NULL ;
char * actuallog = NULL ;
if ( virAsprintf ( & expectname , " %s/commanddata/%s.log " , abs_srcdir ,
testname ) < 0 )
goto cleanup ;
if ( virAsprintf ( & actualname , " %s/commandhelper.log " , abs_builddir ) < 0 )
goto cleanup ;
if ( virFileReadAll ( expectname , 1024 * 64 , & expectlog ) < 0 ) {
fprintf ( stderr , " cannot read %s \n " , expectname ) ;
goto cleanup ;
}
if ( virFileReadAll ( actualname , 1024 * 64 , & actuallog ) < 0 ) {
fprintf ( stderr , " cannot read %s \n " , actualname ) ;
goto cleanup ;
}
2014-07-16 18:19:28 +04:00
if ( prefix ) {
char * tmp = NULL ;
if ( virAsprintf ( & tmp , " %s%s " , prefix , expectlog ) < 0 )
goto cleanup ;
VIR_FREE ( expectlog ) ;
expectlog = tmp ;
}
2010-05-25 15:14:06 +04:00
if ( STRNEQ ( expectlog , actuallog ) ) {
virtTestDifference ( stderr , expectlog , actuallog ) ;
goto cleanup ;
}
ret = 0 ;
2014-03-25 10:53:44 +04:00
cleanup :
2011-05-03 20:37:13 +04:00
if ( actualname )
unlink ( actualname ) ;
2010-05-25 15:14:06 +04:00
VIR_FREE ( actuallog ) ;
VIR_FREE ( actualname ) ;
VIR_FREE ( expectlog ) ;
VIR_FREE ( expectname ) ;
return ret ;
}
/*
* Run program , no args , inherit all ENV , keep CWD .
* Only stdin / out / err open
* No slot for return status must log error .
*/
2010-12-06 13:51:41 +03:00
static int test0 ( const void * unused ATTRIBUTE_UNUSED )
{
2010-05-25 15:14:06 +04:00
virCommandPtr cmd ;
int ret = - 1 ;
cmd = virCommandNew ( abs_builddir " /commandhelper-doesnotexist " ) ;
if ( virCommandRun ( cmd , NULL ) = = 0 )
goto cleanup ;
2010-12-06 14:58:56 +03:00
if ( virGetLastError ( ) = = NULL )
2010-05-25 15:14:06 +04:00
goto cleanup ;
2010-12-06 14:58:56 +03:00
2010-05-25 15:14:06 +04:00
virResetLastError ( ) ;
ret = 0 ;
2014-03-25 10:53:44 +04:00
cleanup :
2010-05-25 15:14:06 +04:00
virCommandFree ( cmd ) ;
return ret ;
}
/*
* Run program , no args , inherit all ENV , keep CWD .
* Only stdin / out / err open
* Capturing return status must not log error .
*/
2010-12-06 13:51:41 +03:00
static int test1 ( const void * unused ATTRIBUTE_UNUSED )
{
2010-05-25 15:14:06 +04:00
virCommandPtr cmd ;
int ret = - 1 ;
int status ;
cmd = virCommandNew ( abs_builddir " /commandhelper-doesnotexist " ) ;
2014-02-20 04:32:19 +04:00
if ( virCommandRun ( cmd , & status ) < 0 )
goto cleanup ;
if ( status ! = EXIT_ENOENT )
goto cleanup ;
virCommandRawStatus ( cmd ) ;
2010-05-25 15:14:06 +04:00
if ( virCommandRun ( cmd , & status ) < 0 )
goto cleanup ;
2014-02-19 05:06:50 +04:00
if ( ! WIFEXITED ( status ) | | WEXITSTATUS ( status ) ! = EXIT_ENOENT )
2010-05-25 15:14:06 +04:00
goto cleanup ;
ret = 0 ;
2014-03-25 10:53:44 +04:00
cleanup :
2010-05-25 15:14:06 +04:00
virCommandFree ( cmd ) ;
return ret ;
}
/*
* Run program ( twice ) , no args , inherit all ENV , keep CWD .
* Only stdin / out / err open
*/
2010-12-06 13:51:41 +03:00
static int test2 ( const void * unused ATTRIBUTE_UNUSED )
{
2010-05-25 15:14:06 +04:00
virCommandPtr cmd = virCommandNew ( abs_builddir " /commandhelper " ) ;
int ret ;
if ( virCommandRun ( cmd , NULL ) < 0 ) {
virErrorPtr err = virGetLastError ( ) ;
printf ( " Cannot run child %s \n " , err - > message ) ;
2010-12-07 00:48:11 +03:00
virCommandFree ( cmd ) ;
2010-05-25 15:14:06 +04:00
return - 1 ;
}
2014-07-16 18:19:28 +04:00
if ( ( ret = checkoutput ( " test2 " , NULL ) ) ! = 0 ) {
2010-05-25 15:14:06 +04:00
virCommandFree ( cmd ) ;
return ret ;
}
if ( virCommandRun ( cmd , NULL ) < 0 ) {
virErrorPtr err = virGetLastError ( ) ;
printf ( " Cannot run child %s \n " , err - > message ) ;
2010-12-07 00:48:11 +03:00
virCommandFree ( cmd ) ;
2010-05-25 15:14:06 +04:00
return - 1 ;
}
virCommandFree ( cmd ) ;
2014-07-16 18:19:28 +04:00
return checkoutput ( " test2 " , NULL ) ;
2010-05-25 15:14:06 +04:00
}
/*
* Run program , no args , inherit all ENV , keep CWD .
* stdin / out / err + two extra FD open
*/
2010-12-06 13:51:41 +03:00
static int test3 ( const void * unused ATTRIBUTE_UNUSED )
{
2010-05-25 15:14:06 +04:00
virCommandPtr cmd = virCommandNew ( abs_builddir " /commandhelper " ) ;
int newfd1 = dup ( STDERR_FILENO ) ;
int newfd2 = dup ( STDERR_FILENO ) ;
int newfd3 = dup ( STDERR_FILENO ) ;
2010-12-07 00:48:11 +03:00
int ret = - 1 ;
2010-05-25 15:14:06 +04:00
2013-07-11 14:31:56 +04:00
virCommandPassFD ( cmd , newfd1 , 0 ) ;
virCommandPassFD ( cmd , newfd3 ,
VIR_COMMAND_PASS_FD_CLOSE_PARENT ) ;
2010-05-25 15:14:06 +04:00
if ( virCommandRun ( cmd , NULL ) < 0 ) {
virErrorPtr err = virGetLastError ( ) ;
printf ( " Cannot run child %s \n " , err - > message ) ;
2010-12-07 00:48:11 +03:00
goto cleanup ;
2010-05-25 15:14:06 +04:00
}
if ( fcntl ( newfd1 , F_GETFL ) < 0 | |
fcntl ( newfd2 , F_GETFL ) < 0 | |
fcntl ( newfd3 , F_GETFL ) > = 0 ) {
puts ( " fds in wrong state " ) ;
2010-12-07 00:48:11 +03:00
goto cleanup ;
2010-05-25 15:14:06 +04:00
}
2014-07-16 18:19:28 +04:00
ret = checkoutput ( " test3 " , NULL ) ;
2010-12-07 00:48:11 +03:00
2014-03-25 10:53:44 +04:00
cleanup :
2010-05-25 15:14:06 +04:00
virCommandFree ( cmd ) ;
2014-01-13 19:50:00 +04:00
/* coverity[double_close] */
2010-05-25 15:14:06 +04:00
VIR_FORCE_CLOSE ( newfd1 ) ;
VIR_FORCE_CLOSE ( newfd2 ) ;
2010-12-07 00:48:11 +03:00
return ret ;
2010-05-25 15:14:06 +04:00
}
/*
* Run program , no args , inherit all ENV , CWD is /
* Only stdin / out / err open .
* Daemonized
*/
2010-12-06 13:51:41 +03:00
static int test4 ( const void * unused ATTRIBUTE_UNUSED )
{
2010-05-25 15:14:06 +04:00
virCommandPtr cmd = virCommandNew ( abs_builddir " /commandhelper " ) ;
2011-08-13 01:54:49 +04:00
char * pidfile = virPidFileBuildPath ( abs_builddir , " commandhelper " ) ;
2010-12-07 00:48:11 +03:00
pid_t pid ;
int ret = - 1 ;
if ( ! pidfile )
goto cleanup ;
2010-05-25 15:14:06 +04:00
virCommandSetPidFile ( cmd , pidfile ) ;
virCommandDaemonize ( cmd ) ;
if ( virCommandRun ( cmd , NULL ) < 0 ) {
virErrorPtr err = virGetLastError ( ) ;
printf ( " Cannot run child %s \n " , err - > message ) ;
2010-12-07 00:48:11 +03:00
goto cleanup ;
2010-05-25 15:14:06 +04:00
}
2011-08-13 01:54:49 +04:00
if ( virPidFileRead ( abs_builddir , " commandhelper " , & pid ) < 0 ) {
2010-05-25 15:14:06 +04:00
printf ( " cannot read pidfile \n " ) ;
2010-12-07 00:48:11 +03:00
goto cleanup ;
2010-05-25 15:14:06 +04:00
}
while ( kill ( pid , 0 ) ! = - 1 )
usleep ( 100 * 1000 ) ;
2014-07-16 18:19:28 +04:00
ret = checkoutput ( " test4 " , NULL ) ;
2010-05-25 15:14:06 +04:00
2014-03-25 10:53:44 +04:00
cleanup :
2010-12-07 00:48:11 +03:00
virCommandFree ( cmd ) ;
2011-05-03 20:37:13 +04:00
if ( pidfile )
unlink ( pidfile ) ;
2010-05-25 15:14:06 +04:00
VIR_FREE ( pidfile ) ;
2010-12-07 00:48:11 +03:00
return ret ;
2010-05-25 15:14:06 +04:00
}
/*
* Run program , no args , inherit filtered ENV , keep CWD .
* Only stdin / out / err open
*/
2010-12-06 13:51:41 +03:00
static int test5 ( const void * unused ATTRIBUTE_UNUSED )
{
2010-05-25 15:14:06 +04:00
virCommandPtr cmd = virCommandNew ( abs_builddir " /commandhelper " ) ;
virCommandAddEnvPassCommon ( cmd ) ;
if ( virCommandRun ( cmd , NULL ) < 0 ) {
virErrorPtr err = virGetLastError ( ) ;
printf ( " Cannot run child %s \n " , err - > message ) ;
2010-12-07 00:48:11 +03:00
virCommandFree ( cmd ) ;
2010-05-25 15:14:06 +04:00
return - 1 ;
}
virCommandFree ( cmd ) ;
2014-07-16 18:19:28 +04:00
return checkoutput ( " test5 " , NULL ) ;
2010-05-25 15:14:06 +04:00
}
/*
* Run program , no args , inherit filtered ENV , keep CWD .
* Only stdin / out / err open
*/
2010-12-06 13:51:41 +03:00
static int test6 ( const void * unused ATTRIBUTE_UNUSED )
{
2010-05-25 15:14:06 +04:00
virCommandPtr cmd = virCommandNew ( abs_builddir " /commandhelper " ) ;
2013-10-09 14:03:02 +04:00
virCommandAddEnvPassBlockSUID ( cmd , " DISPLAY " , NULL ) ;
virCommandAddEnvPassBlockSUID ( cmd , " DOESNOTEXIST " , NULL ) ;
2010-05-25 15:14:06 +04:00
if ( virCommandRun ( cmd , NULL ) < 0 ) {
virErrorPtr err = virGetLastError ( ) ;
printf ( " Cannot run child %s \n " , err - > message ) ;
2010-12-07 00:48:11 +03:00
virCommandFree ( cmd ) ;
2010-05-25 15:14:06 +04:00
return - 1 ;
}
virCommandFree ( cmd ) ;
2014-07-16 18:19:28 +04:00
return checkoutput ( " test6 " , NULL ) ;
2010-05-25 15:14:06 +04:00
}
/*
* Run program , no args , inherit filtered ENV , keep CWD .
* Only stdin / out / err open
*/
2010-12-06 13:51:41 +03:00
static int test7 ( const void * unused ATTRIBUTE_UNUSED )
{
2010-05-25 15:14:06 +04:00
virCommandPtr cmd = virCommandNew ( abs_builddir " /commandhelper " ) ;
virCommandAddEnvPassCommon ( cmd ) ;
2013-10-09 14:03:02 +04:00
virCommandAddEnvPassBlockSUID ( cmd , " DISPLAY " , NULL ) ;
virCommandAddEnvPassBlockSUID ( cmd , " DOESNOTEXIST " , NULL ) ;
2010-05-25 15:14:06 +04:00
if ( virCommandRun ( cmd , NULL ) < 0 ) {
virErrorPtr err = virGetLastError ( ) ;
printf ( " Cannot run child %s \n " , err - > message ) ;
2010-12-07 00:48:11 +03:00
virCommandFree ( cmd ) ;
2010-05-25 15:14:06 +04:00
return - 1 ;
}
virCommandFree ( cmd ) ;
2014-07-16 18:19:28 +04:00
return checkoutput ( " test7 " , NULL ) ;
2010-05-25 15:14:06 +04:00
}
/*
* Run program , no args , inherit filtered ENV , keep CWD .
* Only stdin / out / err open
*/
2010-12-06 13:51:41 +03:00
static int test8 ( const void * unused ATTRIBUTE_UNUSED )
{
2010-05-25 15:14:06 +04:00
virCommandPtr cmd = virCommandNew ( abs_builddir " /commandhelper " ) ;
2012-09-25 03:04:46 +04:00
virCommandAddEnvString ( cmd , " USER=bogus " ) ;
2010-05-25 15:14:06 +04:00
virCommandAddEnvString ( cmd , " LANG=C " ) ;
2012-09-25 03:04:46 +04:00
virCommandAddEnvPair ( cmd , " USER " , " also bogus " ) ;
2010-05-25 15:14:06 +04:00
virCommandAddEnvPair ( cmd , " USER " , " test " ) ;
if ( virCommandRun ( cmd , NULL ) < 0 ) {
virErrorPtr err = virGetLastError ( ) ;
printf ( " Cannot run child %s \n " , err - > message ) ;
2010-12-07 00:48:11 +03:00
virCommandFree ( cmd ) ;
2010-05-25 15:14:06 +04:00
return - 1 ;
}
virCommandFree ( cmd ) ;
2014-07-16 18:19:28 +04:00
return checkoutput ( " test8 " , NULL ) ;
2010-05-25 15:14:06 +04:00
}
/*
* Run program , some args , inherit all ENV , keep CWD .
* Only stdin / out / err open
*/
2010-12-06 13:51:41 +03:00
static int test9 ( const void * unused ATTRIBUTE_UNUSED )
{
2010-05-25 15:14:06 +04:00
virCommandPtr cmd = virCommandNew ( abs_builddir " /commandhelper " ) ;
const char * const args [ ] = { " arg1 " , " arg2 " , NULL } ;
2011-11-10 04:19:33 +04:00
virBuffer buf = VIR_BUFFER_INITIALIZER ;
2010-05-25 15:14:06 +04:00
virCommandAddArg ( cmd , " -version " ) ;
virCommandAddArgPair ( cmd , " -log " , " bar.log " ) ;
virCommandAddArgSet ( cmd , args ) ;
2011-11-10 04:19:33 +04:00
virCommandAddArgBuffer ( cmd , & buf ) ;
virBufferAddLit ( & buf , " arg4 " ) ;
virCommandAddArgBuffer ( cmd , & buf ) ;
virCommandAddArgList ( cmd , " arg5 " , " arg6 " , NULL ) ;
if ( virBufferUse ( & buf ) ) {
printf ( " Buffer not transferred \n " ) ;
virBufferFreeAndReset ( & buf ) ;
virCommandFree ( cmd ) ;
return - 1 ;
}
2010-05-25 15:14:06 +04:00
if ( virCommandRun ( cmd , NULL ) < 0 ) {
virErrorPtr err = virGetLastError ( ) ;
printf ( " Cannot run child %s \n " , err - > message ) ;
2010-12-07 00:48:11 +03:00
virCommandFree ( cmd ) ;
2010-05-25 15:14:06 +04:00
return - 1 ;
}
virCommandFree ( cmd ) ;
2014-07-16 18:19:28 +04:00
return checkoutput ( " test9 " , NULL ) ;
2010-05-25 15:14:06 +04:00
}
/*
* Run program , some args , inherit all ENV , keep CWD .
* Only stdin / out / err open
*/
2010-12-06 13:51:41 +03:00
static int test10 ( const void * unused ATTRIBUTE_UNUSED )
{
2010-05-25 15:14:06 +04:00
virCommandPtr cmd = virCommandNew ( abs_builddir " /commandhelper " ) ;
const char * const args [ ] = {
" -version " , " -log=bar.log " , NULL ,
} ;
virCommandAddArgSet ( cmd , args ) ;
if ( virCommandRun ( cmd , NULL ) < 0 ) {
virErrorPtr err = virGetLastError ( ) ;
printf ( " Cannot run child %s \n " , err - > message ) ;
2010-12-07 00:48:11 +03:00
virCommandFree ( cmd ) ;
2010-05-25 15:14:06 +04:00
return - 1 ;
}
virCommandFree ( cmd ) ;
2014-07-16 18:19:28 +04:00
return checkoutput ( " test10 " , NULL ) ;
2010-05-25 15:14:06 +04:00
}
/*
* Run program , some args , inherit all ENV , keep CWD .
* Only stdin / out / err open
*/
2010-12-06 13:51:41 +03:00
static int test11 ( const void * unused ATTRIBUTE_UNUSED )
{
2010-05-25 15:14:06 +04:00
const char * args [ ] = {
abs_builddir " /commandhelper " ,
" -version " , " -log=bar.log " , NULL ,
} ;
virCommandPtr cmd = virCommandNewArgs ( args ) ;
if ( virCommandRun ( cmd , NULL ) < 0 ) {
virErrorPtr err = virGetLastError ( ) ;
printf ( " Cannot run child %s \n " , err - > message ) ;
2010-12-07 00:48:11 +03:00
virCommandFree ( cmd ) ;
2010-05-25 15:14:06 +04:00
return - 1 ;
}
virCommandFree ( cmd ) ;
2014-07-16 18:19:28 +04:00
return checkoutput ( " test11 " , NULL ) ;
2010-05-25 15:14:06 +04:00
}
/*
* Run program , no args , inherit all ENV , keep CWD .
* Only stdin / out / err open . Set stdin data
*/
2010-12-06 13:51:41 +03:00
static int test12 ( const void * unused ATTRIBUTE_UNUSED )
{
2010-05-25 15:14:06 +04:00
virCommandPtr cmd = virCommandNew ( abs_builddir " /commandhelper " ) ;
virCommandSetInputBuffer ( cmd , " Hello World \n " ) ;
if ( virCommandRun ( cmd , NULL ) < 0 ) {
virErrorPtr err = virGetLastError ( ) ;
printf ( " Cannot run child %s \n " , err - > message ) ;
2010-12-07 00:48:11 +03:00
virCommandFree ( cmd ) ;
2010-05-25 15:14:06 +04:00
return - 1 ;
}
virCommandFree ( cmd ) ;
2014-07-16 18:19:28 +04:00
return checkoutput ( " test12 " , NULL ) ;
2010-05-25 15:14:06 +04:00
}
/*
* Run program , no args , inherit all ENV , keep CWD .
* Only stdin / out / err open . Set stdin data
*/
2010-12-06 13:51:41 +03:00
static int test13 ( const void * unused ATTRIBUTE_UNUSED )
{
2010-05-25 15:14:06 +04:00
virCommandPtr cmd = virCommandNew ( abs_builddir " /commandhelper " ) ;
char * outactual = NULL ;
const char * outexpect = " BEGIN STDOUT \n "
" Hello World \n "
" END STDOUT \n " ;
int ret = - 1 ;
virCommandSetInputBuffer ( cmd , " Hello World \n " ) ;
virCommandSetOutputBuffer ( cmd , & outactual ) ;
if ( virCommandRun ( cmd , NULL ) < 0 ) {
virErrorPtr err = virGetLastError ( ) ;
printf ( " Cannot run child %s \n " , err - > message ) ;
2010-12-07 00:48:11 +03:00
goto cleanup ;
2010-05-25 15:14:06 +04:00
}
2010-12-07 00:48:11 +03:00
if ( ! outactual )
goto cleanup ;
2010-05-25 15:14:06 +04:00
virCommandFree ( cmd ) ;
2010-12-07 00:48:11 +03:00
cmd = NULL ;
2010-05-25 15:14:06 +04:00
if ( ! STREQ ( outactual , outexpect ) ) {
2012-01-28 03:35:14 +04:00
virtTestDifference ( stderr , outexpect , outactual ) ;
2010-05-25 15:14:06 +04:00
goto cleanup ;
}
2014-07-16 18:19:28 +04:00
ret = checkoutput ( " test13 " , NULL ) ;
2010-05-25 15:14:06 +04:00
2014-03-25 10:53:44 +04:00
cleanup :
2010-12-07 00:48:11 +03:00
virCommandFree ( cmd ) ;
2010-05-25 15:14:06 +04:00
VIR_FREE ( outactual ) ;
return ret ;
}
/*
* Run program , no args , inherit all ENV , keep CWD .
* Only stdin / out / err open . Set stdin data
*/
2010-12-06 13:51:41 +03:00
static int test14 ( const void * unused ATTRIBUTE_UNUSED )
{
2010-05-25 15:14:06 +04:00
virCommandPtr cmd = virCommandNew ( abs_builddir " /commandhelper " ) ;
char * outactual = NULL ;
const char * outexpect = " BEGIN STDOUT \n "
" Hello World \n "
" END STDOUT \n " ;
char * erractual = NULL ;
const char * errexpect = " BEGIN STDERR \n "
" Hello World \n "
" END STDERR \n " ;
2012-01-28 02:40:20 +04:00
char * jointactual = NULL ;
const char * jointexpect = " BEGIN STDOUT \n "
" BEGIN STDERR \n "
" Hello World \n "
" Hello World \n "
" END STDOUT \n "
" END STDERR \n " ;
2010-05-25 15:14:06 +04:00
int ret = - 1 ;
virCommandSetInputBuffer ( cmd , " Hello World \n " ) ;
virCommandSetOutputBuffer ( cmd , & outactual ) ;
virCommandSetErrorBuffer ( cmd , & erractual ) ;
if ( virCommandRun ( cmd , NULL ) < 0 ) {
virErrorPtr err = virGetLastError ( ) ;
printf ( " Cannot run child %s \n " , err - > message ) ;
2010-12-07 00:48:11 +03:00
goto cleanup ;
2010-05-25 15:14:06 +04:00
}
2010-12-07 00:48:11 +03:00
if ( ! outactual | | ! erractual )
goto cleanup ;
2010-05-25 15:14:06 +04:00
virCommandFree ( cmd ) ;
2012-01-28 02:40:20 +04:00
cmd = virCommandNew ( abs_builddir " /commandhelper " ) ;
virCommandSetInputBuffer ( cmd , " Hello World \n " ) ;
virCommandSetOutputBuffer ( cmd , & jointactual ) ;
virCommandSetErrorBuffer ( cmd , & jointactual ) ;
if ( virCommandRun ( cmd , NULL ) < 0 ) {
virErrorPtr err = virGetLastError ( ) ;
printf ( " Cannot run child %s \n " , err - > message ) ;
goto cleanup ;
}
if ( ! jointactual )
goto cleanup ;
2010-05-25 15:14:06 +04:00
if ( ! STREQ ( outactual , outexpect ) ) {
2012-01-28 03:35:14 +04:00
virtTestDifference ( stderr , outexpect , outactual ) ;
2010-05-25 15:14:06 +04:00
goto cleanup ;
}
if ( ! STREQ ( erractual , errexpect ) ) {
2012-01-28 03:35:14 +04:00
virtTestDifference ( stderr , errexpect , erractual ) ;
2010-05-25 15:14:06 +04:00
goto cleanup ;
}
2012-01-28 02:40:20 +04:00
if ( ! STREQ ( jointactual , jointexpect ) ) {
virtTestDifference ( stderr , jointexpect , jointactual ) ;
goto cleanup ;
}
2010-05-25 15:14:06 +04:00
2014-07-16 18:19:28 +04:00
ret = checkoutput ( " test14 " , NULL ) ;
2010-05-25 15:14:06 +04:00
2014-03-25 10:53:44 +04:00
cleanup :
2010-12-07 00:48:11 +03:00
virCommandFree ( cmd ) ;
2010-05-25 15:14:06 +04:00
VIR_FREE ( outactual ) ;
VIR_FREE ( erractual ) ;
2012-01-28 02:40:20 +04:00
VIR_FREE ( jointactual ) ;
2010-05-25 15:14:06 +04:00
return ret ;
}
/*
* Run program , no args , inherit all ENV , change CWD .
* Only stdin / out / err open
*/
2010-12-06 13:51:41 +03:00
static int test15 ( const void * unused ATTRIBUTE_UNUSED )
{
2010-05-25 15:14:06 +04:00
virCommandPtr cmd = virCommandNew ( abs_builddir " /commandhelper " ) ;
2010-12-06 15:03:26 +03:00
char * cwd = NULL ;
int ret = - 1 ;
2010-05-25 15:14:06 +04:00
2010-12-06 15:03:26 +03:00
if ( virAsprintf ( & cwd , " %s/commanddata " , abs_srcdir ) < 0 )
goto cleanup ;
virCommandSetWorkingDirectory ( cmd , cwd ) ;
2014-09-03 19:13:21 +04:00
virCommandSetUmask ( cmd , 002 ) ;
2010-05-25 15:14:06 +04:00
if ( virCommandRun ( cmd , NULL ) < 0 ) {
virErrorPtr err = virGetLastError ( ) ;
printf ( " Cannot run child %s \n " , err - > message ) ;
2010-12-06 15:03:26 +03:00
goto cleanup ;
2010-05-25 15:14:06 +04:00
}
2014-07-16 18:19:28 +04:00
ret = checkoutput ( " test15 " , NULL ) ;
2010-12-06 15:03:26 +03:00
2014-03-25 10:53:44 +04:00
cleanup :
2010-12-06 15:03:26 +03:00
VIR_FREE ( cwd ) ;
2010-05-25 15:14:06 +04:00
virCommandFree ( cmd ) ;
2010-12-06 15:03:26 +03:00
return ret ;
2010-05-25 15:14:06 +04:00
}
/*
* Don ' t run program ; rather , log what would be run .
*/
2010-12-06 13:51:41 +03:00
static int test16 ( const void * unused ATTRIBUTE_UNUSED )
{
2011-07-28 19:51:26 +04:00
virCommandPtr cmd = virCommandNew ( " true " ) ;
2010-05-25 15:14:06 +04:00
char * outactual = NULL ;
2012-08-28 22:11:45 +04:00
const char * outexpect = " A=B C='D E' true F 'G H' " ;
2010-05-25 15:14:06 +04:00
int ret = - 1 ;
int fd = - 1 ;
virCommandAddEnvPair ( cmd , " A " , " B " ) ;
2012-08-28 22:11:45 +04:00
virCommandAddEnvPair ( cmd , " C " , " D E " ) ;
virCommandAddArg ( cmd , " F " ) ;
virCommandAddArg ( cmd , " G H " ) ;
2010-05-25 15:14:06 +04:00
if ( ( outactual = virCommandToString ( cmd ) ) = = NULL ) {
virErrorPtr err = virGetLastError ( ) ;
printf ( " Cannot convert to string: %s \n " , err - > message ) ;
2010-12-07 00:48:11 +03:00
goto cleanup ;
2010-05-25 15:14:06 +04:00
}
if ( ( fd = open ( abs_builddir " /commandhelper.log " ,
O_CREAT | O_TRUNC | O_WRONLY , 0600 ) ) < 0 ) {
2012-10-17 13:23:12 +04:00
printf ( " Cannot open log file: %s \n " , strerror ( errno ) ) ;
2010-05-25 15:14:06 +04:00
goto cleanup ;
}
virCommandWriteArgLog ( cmd , fd ) ;
if ( VIR_CLOSE ( fd ) < 0 ) {
2012-10-17 13:23:12 +04:00
printf ( " Cannot close log file: %s \n " , strerror ( errno ) ) ;
2010-05-25 15:14:06 +04:00
goto cleanup ;
}
if ( ! STREQ ( outactual , outexpect ) ) {
2012-01-28 03:35:14 +04:00
virtTestDifference ( stderr , outexpect , outactual ) ;
2010-05-25 15:14:06 +04:00
goto cleanup ;
}
2010-12-07 00:48:11 +03:00
2014-07-16 18:19:28 +04:00
ret = checkoutput ( " test16 " , NULL ) ;
2010-05-25 15:14:06 +04:00
2014-03-25 10:53:44 +04:00
cleanup :
2010-12-07 00:48:11 +03:00
virCommandFree ( cmd ) ;
2010-05-25 15:14:06 +04:00
VIR_FORCE_CLOSE ( fd ) ;
VIR_FREE ( outactual ) ;
return ret ;
}
2010-12-04 00:14:16 +03:00
/*
* Test string handling when no output is present .
*/
static int test17 ( const void * unused ATTRIBUTE_UNUSED )
{
2011-07-28 19:51:26 +04:00
virCommandPtr cmd = virCommandNew ( " true " ) ;
2010-12-04 00:14:16 +03:00
int ret = - 1 ;
char * outbuf ;
2013-01-22 18:15:42 +04:00
char * errbuf = NULL ;
2010-12-04 00:14:16 +03:00
virCommandSetOutputBuffer ( cmd , & outbuf ) ;
if ( outbuf ! = NULL ) {
puts ( " buffer not sanitized at registration " ) ;
goto cleanup ;
}
if ( virCommandRun ( cmd , NULL ) < 0 ) {
virErrorPtr err = virGetLastError ( ) ;
printf ( " Cannot run child %s \n " , err - > message ) ;
goto cleanup ;
}
2014-03-25 21:03:19 +04:00
sa_assert ( outbuf ) ;
2013-01-14 19:35:45 +04:00
if ( * outbuf ) {
2010-12-04 00:14:16 +03:00
puts ( " output buffer is not an allocated empty string " ) ;
goto cleanup ;
}
VIR_FREE ( outbuf ) ;
2013-05-03 16:52:21 +04:00
if ( VIR_STRDUP ( outbuf , " should not be leaked " ) < 0 ) {
2010-12-04 00:14:16 +03:00
puts ( " test framework failure " ) ;
goto cleanup ;
}
virCommandSetErrorBuffer ( cmd , & errbuf ) ;
if ( errbuf ! = NULL ) {
puts ( " buffer not sanitized at registration " ) ;
goto cleanup ;
}
if ( virCommandRun ( cmd , NULL ) < 0 ) {
virErrorPtr err = virGetLastError ( ) ;
printf ( " Cannot run child %s \n " , err - > message ) ;
goto cleanup ;
}
2013-01-14 19:35:45 +04:00
if ( * outbuf | | * errbuf ) {
2010-12-04 00:14:16 +03:00
puts ( " output buffers are not allocated empty strings " ) ;
goto cleanup ;
}
ret = 0 ;
2014-03-25 10:53:44 +04:00
cleanup :
2010-12-04 00:14:16 +03:00
virCommandFree ( cmd ) ;
VIR_FREE ( outbuf ) ;
VIR_FREE ( errbuf ) ;
return ret ;
}
2010-12-21 21:49:49 +03:00
/*
* Run long - running daemon , to ensure no hang .
*/
static int test18 ( const void * unused ATTRIBUTE_UNUSED )
{
virCommandPtr cmd = virCommandNewArgList ( " sleep " , " 100 " , NULL ) ;
2011-08-13 01:54:49 +04:00
char * pidfile = virPidFileBuildPath ( abs_builddir , " commandhelper " ) ;
2010-12-21 21:49:49 +03:00
pid_t pid ;
int ret = - 1 ;
if ( ! pidfile )
goto cleanup ;
virCommandSetPidFile ( cmd , pidfile ) ;
virCommandDaemonize ( cmd ) ;
alarm ( 5 ) ;
if ( virCommandRun ( cmd , NULL ) < 0 ) {
virErrorPtr err = virGetLastError ( ) ;
printf ( " Cannot run child %s \n " , err - > message ) ;
goto cleanup ;
}
alarm ( 0 ) ;
2011-08-13 01:54:49 +04:00
if ( virPidFileRead ( abs_builddir , " commandhelper " , & pid ) < 0 ) {
2010-12-21 21:49:49 +03:00
printf ( " cannot read pidfile \n " ) ;
goto cleanup ;
}
2011-03-23 01:22:37 +03:00
virCommandFree ( cmd ) ;
cmd = NULL ;
if ( kill ( pid , 0 ) ! = 0 ) {
printf ( " daemon should still be running \n " ) ;
goto cleanup ;
}
2010-12-21 21:49:49 +03:00
while ( kill ( pid , SIGINT ) ! = - 1 )
usleep ( 100 * 1000 ) ;
ret = 0 ;
2014-03-25 10:53:44 +04:00
cleanup :
2010-12-21 21:49:49 +03:00
virCommandFree ( cmd ) ;
2011-05-03 20:37:13 +04:00
if ( pidfile )
unlink ( pidfile ) ;
2010-12-21 21:49:49 +03:00
VIR_FREE ( pidfile ) ;
return ret ;
}
2011-03-23 01:22:37 +03:00
/*
* Asynchronously run long - running daemon , to ensure no hang .
*/
static int test19 ( const void * unused ATTRIBUTE_UNUSED )
{
virCommandPtr cmd = virCommandNewArgList ( " sleep " , " 100 " , NULL ) ;
pid_t pid ;
int ret = - 1 ;
alarm ( 5 ) ;
if ( virCommandRunAsync ( cmd , & pid ) < 0 ) {
virErrorPtr err = virGetLastError ( ) ;
printf ( " Cannot run child %s \n " , err - > message ) ;
goto cleanup ;
}
if ( kill ( pid , 0 ) ! = 0 ) {
printf ( " Child should still be running " ) ;
goto cleanup ;
}
virCommandAbort ( cmd ) ;
if ( kill ( pid , 0 ) = = 0 ) {
printf ( " Child should be aborted " ) ;
goto cleanup ;
}
alarm ( 0 ) ;
ret = 0 ;
2014-03-25 10:53:44 +04:00
cleanup :
2011-03-23 01:22:37 +03:00
virCommandFree ( cmd ) ;
return ret ;
}
2010-12-21 21:49:49 +03:00
2012-06-01 01:50:07 +04:00
/*
* Run program , no args , inherit all ENV , keep CWD .
* Ignore huge stdin data , to provoke SIGPIPE or EPIPE in parent .
*/
static int test20 ( const void * unused ATTRIBUTE_UNUSED )
{
virCommandPtr cmd = virCommandNewArgList ( abs_builddir " /commandhelper " ,
" --close-stdin " , NULL ) ;
char * buf ;
int ret = - 1 ;
struct sigaction sig_action ;
sig_action . sa_handler = SIG_IGN ;
sig_action . sa_flags = 0 ;
sigemptyset ( & sig_action . sa_mask ) ;
sigaction ( SIGPIPE , & sig_action , NULL ) ;
2013-07-04 14:20:21 +04:00
if ( virAsprintf ( & buf , " 1 \n %100000d \n " , 2 ) < 0 )
2012-06-01 01:50:07 +04:00
goto cleanup ;
virCommandSetInputBuffer ( cmd , buf ) ;
if ( virCommandRun ( cmd , NULL ) < 0 ) {
virErrorPtr err = virGetLastError ( ) ;
printf ( " Cannot run child %s \n " , err - > message ) ;
goto cleanup ;
}
2014-07-16 18:19:28 +04:00
ret = checkoutput ( " test20 " , NULL ) ;
2014-03-25 10:53:44 +04:00
cleanup :
2012-06-01 01:50:07 +04:00
virCommandFree ( cmd ) ;
VIR_FREE ( buf ) ;
return ret ;
}
2011-05-11 19:51:30 +04:00
static const char * const newenv [ ] = {
" PATH=/usr/bin:/bin " ,
" HOSTNAME=test " ,
" LANG=C " ,
" HOME=/home/test " ,
" USER=test " ,
" LOGNAME=test "
" TMPDIR=/tmp " ,
" DISPLAY=:0.0 " ,
NULL
} ;
2013-01-16 21:55:06 +04:00
static int test21 ( const void * unused ATTRIBUTE_UNUSED )
{
virCommandPtr cmd = virCommandNew ( abs_builddir " /commandhelper " ) ;
int ret = - 1 ;
const char * wrbuf = " Hello world \n " ;
char * outbuf = NULL , * errbuf = NULL ;
2014-01-20 15:27:29 +04:00
const char * outbufExpected = " BEGIN STDOUT \n "
2013-01-16 21:55:06 +04:00
" Hello world \n "
" END STDOUT \n " ;
2014-01-20 15:27:29 +04:00
const char * errbufExpected = " BEGIN STDERR \n "
2013-01-16 21:55:06 +04:00
" Hello world \n "
" END STDERR \n " ;
virCommandSetInputBuffer ( cmd , wrbuf ) ;
virCommandSetOutputBuffer ( cmd , & outbuf ) ;
virCommandSetErrorBuffer ( cmd , & errbuf ) ;
virCommandDoAsyncIO ( cmd ) ;
if ( virCommandRunAsync ( cmd , NULL ) < 0 ) {
virErrorPtr err = virGetLastError ( ) ;
printf ( " Cannot run child %s \n " , err - > message ) ;
goto cleanup ;
}
if ( virCommandWait ( cmd , NULL ) < 0 )
goto cleanup ;
if ( virTestGetVerbose ( ) )
printf ( " STDOUT:%s \n STDERR:%s \n " , NULLSTR ( outbuf ) , NULLSTR ( errbuf ) ) ;
if ( STRNEQ ( outbuf , outbufExpected ) ) {
virtTestDifference ( stderr , outbufExpected , outbuf ) ;
goto cleanup ;
}
if ( STRNEQ ( errbuf , errbufExpected ) ) {
virtTestDifference ( stderr , errbufExpected , errbuf ) ;
goto cleanup ;
}
2014-07-16 18:19:28 +04:00
ret = checkoutput ( " test21 " , NULL ) ;
2014-03-25 10:53:44 +04:00
cleanup :
2013-01-16 21:55:06 +04:00
VIR_FREE ( outbuf ) ;
VIR_FREE ( errbuf ) ;
virCommandFree ( cmd ) ;
return ret ;
}
2014-02-19 05:06:50 +04:00
static int
test22 ( const void * unused ATTRIBUTE_UNUSED )
{
int ret = - 1 ;
virCommandPtr cmd ;
int status = - 1 ;
cmd = virCommandNewArgList ( " /bin/sh " , " -c " , " exit 3 " , NULL ) ;
2014-02-20 04:32:19 +04:00
if ( virCommandRun ( cmd , & status ) < 0 ) {
virErrorPtr err = virGetLastError ( ) ;
printf ( " Cannot run child %s \n " , err - > message ) ;
goto cleanup ;
}
if ( status ! = 3 ) {
printf ( " Unexpected status %d \n " , status ) ;
goto cleanup ;
}
virCommandRawStatus ( cmd ) ;
2014-02-19 05:06:50 +04:00
if ( virCommandRun ( cmd , & status ) < 0 ) {
virErrorPtr err = virGetLastError ( ) ;
printf ( " Cannot run child %s \n " , err - > message ) ;
goto cleanup ;
}
if ( ! WIFEXITED ( status ) | | WEXITSTATUS ( status ) ! = 3 ) {
printf ( " Unexpected status %d \n " , status ) ;
goto cleanup ;
}
virCommandFree ( cmd ) ;
cmd = virCommandNewArgList ( " /bin/sh " , " -c " , " kill -9 $$ " , NULL ) ;
2014-02-20 04:32:19 +04:00
if ( virCommandRun ( cmd , & status ) = = 0 ) {
printf ( " Death by signal not detected, status %d \n " , status ) ;
goto cleanup ;
}
virCommandRawStatus ( cmd ) ;
2014-02-19 05:06:50 +04:00
if ( virCommandRun ( cmd , & status ) < 0 ) {
virErrorPtr err = virGetLastError ( ) ;
printf ( " Cannot run child %s \n " , err - > message ) ;
goto cleanup ;
}
if ( ! WIFSIGNALED ( status ) | | WTERMSIG ( status ) ! = SIGKILL ) {
printf ( " Unexpected status %d \n " , status ) ;
goto cleanup ;
}
ret = 0 ;
2014-03-25 10:53:44 +04:00
cleanup :
2014-02-19 05:06:50 +04:00
virCommandFree ( cmd ) ;
return ret ;
}
2014-02-20 07:04:40 +04:00
static int
test23 ( const void * unused ATTRIBUTE_UNUSED )
{
/* Not strictly a virCommand test, but this is the easiest place
* to test this lower - level interface . It takes a double fork to
* test virProcessExitWithStatus . */
int ret = - 1 ;
int status = - 1 ;
pid_t pid ;
virFork: simplify semantics
The old semantics of virFork() violates the priciple of good
usability: it requires the caller to check the pid argument
after use, *even when virFork returned -1*, in order to properly
abort a child process that failed setup done immediately after
fork() - that is, the caller must call _exit() in the child.
While uses in virfile.c did this correctly, uses in 'virsh
lxc-enter-namespace' and 'virt-login-shell' would happily return
from the calling function in both the child and the parent,
leading to very confusing results. [Thankfully, I found the
problem by inspection, and can't actually trigger the double
return on error without an LD_PRELOAD library.]
It is much better if the semantics of virFork are impossible
to abuse. Looking at virFork(), the parent could only ever
return -1 with a non-negative pid if it misused pthread_sigmask,
but this never happens. Up until this patch series, the child
could return -1 with non-negative pid if it fails to set up
signals correctly, but we recently fixed that to make the child
call _exit() at that point instead of forcing the caller to do
it. Thus, the return value and contents of the pid argument are
now redundant (a -1 return now happens only for failure to fork,
a child 0 return only happens for a successful 0 pid, and a
parent 0 return only happens for a successful non-zero pid),
so we might as well return the pid directly rather than an
integer of whether it succeeded or failed; this is also good
from the interface design perspective as users are already
familiar with fork() semantics.
One last change in this patch: before returning the pid directly,
I found cases where using virProcessWait unconditionally on a
cleanup path of a virFork's -1 pid return would be nicer if there
were a way to avoid it overwriting an earlier message. While
such paths are a bit harder to come by with my change to a direct
pid return, I decided to keep the virProcessWait change in this
patch.
* src/util/vircommand.h (virFork): Change signature.
* src/util/vircommand.c (virFork): Guarantee that child will only
return on success, to simplify callers. Return pid rather than
status, now that the situations are always the same.
(virExec): Adjust caller, also avoid open-coding process death.
* src/util/virprocess.c (virProcessWait): Tweak semantics when pid
is -1.
(virProcessRunInMountNamespace): Adjust caller.
* src/util/virfile.c (virFileAccessibleAs, virFileOpenForked)
(virDirCreate): Likewise.
* tools/virt-login-shell.c (main): Likewise.
* tools/virsh-domain.c (cmdLxcEnterNamespace): Likewise.
* tests/commandtest.c (test23): Likewise.
Signed-off-by: Eric Blake <eblake@redhat.com>
2013-12-22 04:54:33 +04:00
if ( ( pid = virFork ( ) ) < 0 )
2014-02-20 07:04:40 +04:00
goto cleanup ;
if ( pid = = 0 ) {
virFork: simplify semantics
The old semantics of virFork() violates the priciple of good
usability: it requires the caller to check the pid argument
after use, *even when virFork returned -1*, in order to properly
abort a child process that failed setup done immediately after
fork() - that is, the caller must call _exit() in the child.
While uses in virfile.c did this correctly, uses in 'virsh
lxc-enter-namespace' and 'virt-login-shell' would happily return
from the calling function in both the child and the parent,
leading to very confusing results. [Thankfully, I found the
problem by inspection, and can't actually trigger the double
return on error without an LD_PRELOAD library.]
It is much better if the semantics of virFork are impossible
to abuse. Looking at virFork(), the parent could only ever
return -1 with a non-negative pid if it misused pthread_sigmask,
but this never happens. Up until this patch series, the child
could return -1 with non-negative pid if it fails to set up
signals correctly, but we recently fixed that to make the child
call _exit() at that point instead of forcing the caller to do
it. Thus, the return value and contents of the pid argument are
now redundant (a -1 return now happens only for failure to fork,
a child 0 return only happens for a successful 0 pid, and a
parent 0 return only happens for a successful non-zero pid),
so we might as well return the pid directly rather than an
integer of whether it succeeded or failed; this is also good
from the interface design perspective as users are already
familiar with fork() semantics.
One last change in this patch: before returning the pid directly,
I found cases where using virProcessWait unconditionally on a
cleanup path of a virFork's -1 pid return would be nicer if there
were a way to avoid it overwriting an earlier message. While
such paths are a bit harder to come by with my change to a direct
pid return, I decided to keep the virProcessWait change in this
patch.
* src/util/vircommand.h (virFork): Change signature.
* src/util/vircommand.c (virFork): Guarantee that child will only
return on success, to simplify callers. Return pid rather than
status, now that the situations are always the same.
(virExec): Adjust caller, also avoid open-coding process death.
* src/util/virprocess.c (virProcessWait): Tweak semantics when pid
is -1.
(virProcessRunInMountNamespace): Adjust caller.
* src/util/virfile.c (virFileAccessibleAs, virFileOpenForked)
(virDirCreate): Likewise.
* tools/virt-login-shell.c (main): Likewise.
* tools/virsh-domain.c (cmdLxcEnterNamespace): Likewise.
* tests/commandtest.c (test23): Likewise.
Signed-off-by: Eric Blake <eblake@redhat.com>
2013-12-22 04:54:33 +04:00
if ( ( pid = virFork ( ) ) < 0 )
2014-02-20 07:04:40 +04:00
_exit ( EXIT_FAILURE ) ;
if ( pid = = 0 )
_exit ( 42 ) ;
2014-02-20 07:23:44 +04:00
if ( virProcessWait ( pid , & status , true ) < 0 )
2014-02-20 07:04:40 +04:00
_exit ( EXIT_FAILURE ) ;
virProcessExitWithStatus ( status ) ;
_exit ( EXIT_FAILURE ) ;
}
2014-02-20 07:23:44 +04:00
if ( virProcessWait ( pid , & status , true ) < 0 )
2014-02-20 07:04:40 +04:00
goto cleanup ;
if ( ! WIFEXITED ( status ) | | WEXITSTATUS ( status ) ! = 42 ) {
printf ( " Unexpected status %d \n " , status ) ;
goto cleanup ;
}
virFork: simplify semantics
The old semantics of virFork() violates the priciple of good
usability: it requires the caller to check the pid argument
after use, *even when virFork returned -1*, in order to properly
abort a child process that failed setup done immediately after
fork() - that is, the caller must call _exit() in the child.
While uses in virfile.c did this correctly, uses in 'virsh
lxc-enter-namespace' and 'virt-login-shell' would happily return
from the calling function in both the child and the parent,
leading to very confusing results. [Thankfully, I found the
problem by inspection, and can't actually trigger the double
return on error without an LD_PRELOAD library.]
It is much better if the semantics of virFork are impossible
to abuse. Looking at virFork(), the parent could only ever
return -1 with a non-negative pid if it misused pthread_sigmask,
but this never happens. Up until this patch series, the child
could return -1 with non-negative pid if it fails to set up
signals correctly, but we recently fixed that to make the child
call _exit() at that point instead of forcing the caller to do
it. Thus, the return value and contents of the pid argument are
now redundant (a -1 return now happens only for failure to fork,
a child 0 return only happens for a successful 0 pid, and a
parent 0 return only happens for a successful non-zero pid),
so we might as well return the pid directly rather than an
integer of whether it succeeded or failed; this is also good
from the interface design perspective as users are already
familiar with fork() semantics.
One last change in this patch: before returning the pid directly,
I found cases where using virProcessWait unconditionally on a
cleanup path of a virFork's -1 pid return would be nicer if there
were a way to avoid it overwriting an earlier message. While
such paths are a bit harder to come by with my change to a direct
pid return, I decided to keep the virProcessWait change in this
patch.
* src/util/vircommand.h (virFork): Change signature.
* src/util/vircommand.c (virFork): Guarantee that child will only
return on success, to simplify callers. Return pid rather than
status, now that the situations are always the same.
(virExec): Adjust caller, also avoid open-coding process death.
* src/util/virprocess.c (virProcessWait): Tweak semantics when pid
is -1.
(virProcessRunInMountNamespace): Adjust caller.
* src/util/virfile.c (virFileAccessibleAs, virFileOpenForked)
(virDirCreate): Likewise.
* tools/virt-login-shell.c (main): Likewise.
* tools/virsh-domain.c (cmdLxcEnterNamespace): Likewise.
* tests/commandtest.c (test23): Likewise.
Signed-off-by: Eric Blake <eblake@redhat.com>
2013-12-22 04:54:33 +04:00
if ( ( pid = virFork ( ) ) < 0 )
2014-02-20 07:04:40 +04:00
goto cleanup ;
if ( pid = = 0 ) {
virFork: simplify semantics
The old semantics of virFork() violates the priciple of good
usability: it requires the caller to check the pid argument
after use, *even when virFork returned -1*, in order to properly
abort a child process that failed setup done immediately after
fork() - that is, the caller must call _exit() in the child.
While uses in virfile.c did this correctly, uses in 'virsh
lxc-enter-namespace' and 'virt-login-shell' would happily return
from the calling function in both the child and the parent,
leading to very confusing results. [Thankfully, I found the
problem by inspection, and can't actually trigger the double
return on error without an LD_PRELOAD library.]
It is much better if the semantics of virFork are impossible
to abuse. Looking at virFork(), the parent could only ever
return -1 with a non-negative pid if it misused pthread_sigmask,
but this never happens. Up until this patch series, the child
could return -1 with non-negative pid if it fails to set up
signals correctly, but we recently fixed that to make the child
call _exit() at that point instead of forcing the caller to do
it. Thus, the return value and contents of the pid argument are
now redundant (a -1 return now happens only for failure to fork,
a child 0 return only happens for a successful 0 pid, and a
parent 0 return only happens for a successful non-zero pid),
so we might as well return the pid directly rather than an
integer of whether it succeeded or failed; this is also good
from the interface design perspective as users are already
familiar with fork() semantics.
One last change in this patch: before returning the pid directly,
I found cases where using virProcessWait unconditionally on a
cleanup path of a virFork's -1 pid return would be nicer if there
were a way to avoid it overwriting an earlier message. While
such paths are a bit harder to come by with my change to a direct
pid return, I decided to keep the virProcessWait change in this
patch.
* src/util/vircommand.h (virFork): Change signature.
* src/util/vircommand.c (virFork): Guarantee that child will only
return on success, to simplify callers. Return pid rather than
status, now that the situations are always the same.
(virExec): Adjust caller, also avoid open-coding process death.
* src/util/virprocess.c (virProcessWait): Tweak semantics when pid
is -1.
(virProcessRunInMountNamespace): Adjust caller.
* src/util/virfile.c (virFileAccessibleAs, virFileOpenForked)
(virDirCreate): Likewise.
* tools/virt-login-shell.c (main): Likewise.
* tools/virsh-domain.c (cmdLxcEnterNamespace): Likewise.
* tests/commandtest.c (test23): Likewise.
Signed-off-by: Eric Blake <eblake@redhat.com>
2013-12-22 04:54:33 +04:00
if ( ( pid = virFork ( ) ) < 0 )
2014-02-20 07:04:40 +04:00
_exit ( EXIT_FAILURE ) ;
if ( pid = = 0 ) {
raise ( SIGKILL ) ;
_exit ( EXIT_FAILURE ) ;
}
2014-02-20 07:23:44 +04:00
if ( virProcessWait ( pid , & status , true ) < 0 )
2014-02-20 07:04:40 +04:00
_exit ( EXIT_FAILURE ) ;
virProcessExitWithStatus ( status ) ;
_exit ( EXIT_FAILURE ) ;
}
2014-02-20 07:23:44 +04:00
if ( virProcessWait ( pid , & status , true ) < 0 )
2014-02-20 07:04:40 +04:00
goto cleanup ;
if ( ! WIFSIGNALED ( status ) | | WTERMSIG ( status ) ! = SIGKILL ) {
printf ( " Unexpected status %d \n " , status ) ;
goto cleanup ;
}
ret = 0 ;
2014-03-25 10:53:44 +04:00
cleanup :
2014-02-20 07:04:40 +04:00
return ret ;
}
2014-07-15 19:07:02 +04:00
static int test24 ( const void * unused ATTRIBUTE_UNUSED )
{
char * pidfile = virPidFileBuildPath ( abs_builddir , " commandhelper " ) ;
char * prefix = NULL ;
int newfd1 = dup ( STDERR_FILENO ) ;
int newfd2 = dup ( STDERR_FILENO ) ;
int newfd3 = dup ( STDERR_FILENO ) ;
int ret = - 1 ;
pid_t pid ;
virCommandPtr cmd = virCommandNew ( abs_builddir " /commandhelper " ) ;
if ( ! pidfile )
goto cleanup ;
if ( VIR_CLOSE ( newfd1 ) < 0 )
printf ( " Cannot close fd %d \n " , newfd1 ) ;
virCommandSetPidFile ( cmd , pidfile ) ;
virCommandDaemonize ( cmd ) ;
virCommandPassFD ( cmd , newfd2 , VIR_COMMAND_PASS_FD_CLOSE_PARENT ) ;
virCommandPassFD ( cmd , newfd3 , VIR_COMMAND_PASS_FD_CLOSE_PARENT ) ;
virCommandPassListenFDs ( cmd ) ;
if ( virCommandRun ( cmd , NULL ) < 0 ) {
virErrorPtr err = virGetLastError ( ) ;
printf ( " Cannot run child %s \n " , err - > message ) ;
goto cleanup ;
}
if ( virPidFileRead ( abs_builddir , " commandhelper " , & pid ) < 0 ) {
printf ( " cannot read pidfile \n " ) ;
goto cleanup ;
}
if ( virAsprintf ( & prefix ,
" ENV:LISTEN_FDS=2 \n ENV:LISTEN_PID=%u \n " ,
pid ) < 0 )
goto cleanup ;
while ( kill ( pid , 0 ) ! = - 1 )
usleep ( 100 * 1000 ) ;
ret = checkoutput ( " test24 " , prefix ) ;
cleanup :
if ( pidfile )
unlink ( pidfile ) ;
VIR_FREE ( pidfile ) ;
virCommandFree ( cmd ) ;
2014-08-22 18:30:44 +04:00
VIR_FORCE_CLOSE ( newfd1 ) ;
2014-07-15 19:07:02 +04:00
/* coverity[double_close] */
VIR_FORCE_CLOSE ( newfd2 ) ;
VIR_FORCE_CLOSE ( newfd3 ) ;
return ret ;
}
2013-01-16 14:58:00 +04:00
static void virCommandThreadWorker ( void * opaque )
{
virCommandTestDataPtr test = opaque ;
virMutexLock ( & test - > lock ) ;
while ( ! test - > quit ) {
virMutexUnlock ( & test - > lock ) ;
if ( virEventRunDefaultImpl ( ) < 0 ) {
test - > quit = true ;
break ;
}
virMutexLock ( & test - > lock ) ;
}
test - > running = false ;
virMutexUnlock ( & test - > lock ) ;
return ;
}
static void
virCommandTestFreeTimer ( int timer ATTRIBUTE_UNUSED ,
void * opaque ATTRIBUTE_UNUSED )
{
/* nothing to be done here */
}
2010-05-25 15:14:06 +04:00
static int
2011-04-29 20:21:20 +04:00
mymain ( void )
2010-05-25 15:14:06 +04:00
{
int ret = 0 ;
2011-01-29 00:22:39 +03:00
int fd ;
2013-01-16 14:58:00 +04:00
virCommandTestDataPtr test = NULL ;
int timer = - 1 ;
2013-01-14 19:35:45 +04:00
int virinitret ;
2013-01-16 14:58:00 +04:00
if ( virThreadInitialize ( ) < 0 )
return EXIT_FAILURE ;
2010-05-25 15:14:06 +04:00
if ( chdir ( " /tmp " ) < 0 )
2012-03-22 15:33:35 +04:00
return EXIT_FAILURE ;
2010-05-25 15:14:06 +04:00
2014-09-03 19:13:21 +04:00
umask ( 022 ) ;
2011-02-24 15:12:27 +03:00
setpgid ( 0 , 0 ) ;
2012-04-28 01:25:35 +04:00
ignore_value ( setsid ( ) ) ;
2011-02-24 15:12:27 +03:00
2012-01-07 01:07:23 +04:00
/* Our test expects particular fd values; to get that, we must not
* leak fds that we inherited from a lazy parent . At the same
* time , virInitialize may open some fds ( perhaps via third - party
* libraries that it uses ) , and we must not kill off an fd that
* this process opens as it might break expectations of a
* pthread_atfork handler , as well as interfering with our tests
* trying to ensure we aren ' t leaking to our children . The
* solution is to do things in two phases - reserve the fds we
* want by overwriting any externally inherited fds , then
* initialize , then clear the slots for testing . */
if ( ( fd = open ( " /dev/null " , O_RDONLY ) ) < 0 | |
dup2 ( fd , 3 ) < 0 | |
dup2 ( fd , 4 ) < 0 | |
dup2 ( fd , 5 ) < 0 | |
2013-02-05 20:15:20 +04:00
dup2 ( fd , 6 ) < 0 | |
dup2 ( fd , 7 ) < 0 | |
dup2 ( fd , 8 ) < 0 | |
2013-01-14 19:35:45 +04:00
( fd > 8 & & VIR_CLOSE ( fd ) < 0 ) ) {
VIR_FORCE_CLOSE ( fd ) ;
2012-01-07 01:07:23 +04:00
return EXIT_FAILURE ;
2013-01-14 19:35:45 +04:00
}
2011-08-25 15:05:54 +04:00
/* Prime the debug/verbose settings from the env vars,
* since we ' re about to reset ' environ ' */
2012-04-28 01:25:35 +04:00
ignore_value ( virTestGetDebug ( ) ) ;
ignore_value ( virTestGetVerbose ( ) ) ;
2011-08-25 15:05:54 +04:00
2013-01-14 19:35:45 +04:00
/* Make sure to not leak fd's */
virinitret = virInitialize ( ) ;
2012-01-07 01:07:23 +04:00
/* Phase two of killing interfering fds; see above. */
2013-01-14 19:35:45 +04:00
/* coverity[overwrite_var] - silence the obvious */
2011-01-29 00:22:39 +03:00
fd = 3 ;
VIR_FORCE_CLOSE ( fd ) ;
fd = 4 ;
VIR_FORCE_CLOSE ( fd ) ;
fd = 5 ;
VIR_FORCE_CLOSE ( fd ) ;
2013-02-05 20:15:20 +04:00
fd = 6 ;
VIR_FORCE_CLOSE ( fd ) ;
fd = 7 ;
VIR_FORCE_CLOSE ( fd ) ;
fd = 8 ;
VIR_FORCE_CLOSE ( fd ) ;
2010-12-11 01:30:56 +03:00
2013-01-14 19:35:45 +04:00
if ( virinitret < 0 )
return EXIT_FAILURE ;
2013-01-16 14:58:00 +04:00
virEventRegisterDefaultImpl ( ) ;
2013-07-04 14:20:21 +04:00
if ( VIR_ALLOC ( test ) < 0 )
2013-01-16 14:58:00 +04:00
goto cleanup ;
if ( virMutexInit ( & test - > lock ) < 0 ) {
printf ( " Unable to init mutex: %d \n " , errno ) ;
goto cleanup ;
}
virMutexLock ( & test - > lock ) ;
if ( virThreadCreate ( & test - > thread ,
true ,
virCommandThreadWorker ,
test ) < 0 ) {
virMutexUnlock ( & test - > lock ) ;
goto cleanup ;
}
test - > running = true ;
virMutexUnlock ( & test - > lock ) ;
2010-05-25 15:14:06 +04:00
environ = ( char * * ) newenv ;
# define DO_TEST(NAME) \
if ( virtTestRun ( " Command Exec " # NAME " test " , \
2013-09-20 22:13:35 +04:00
NAME , NULL ) < 0 ) \
2010-05-25 15:14:06 +04:00
ret = - 1
DO_TEST ( test0 ) ;
DO_TEST ( test1 ) ;
DO_TEST ( test2 ) ;
DO_TEST ( test3 ) ;
DO_TEST ( test4 ) ;
DO_TEST ( test5 ) ;
DO_TEST ( test6 ) ;
DO_TEST ( test7 ) ;
DO_TEST ( test8 ) ;
DO_TEST ( test9 ) ;
DO_TEST ( test10 ) ;
DO_TEST ( test11 ) ;
DO_TEST ( test12 ) ;
DO_TEST ( test13 ) ;
DO_TEST ( test14 ) ;
DO_TEST ( test15 ) ;
DO_TEST ( test16 ) ;
2010-12-04 00:14:16 +03:00
DO_TEST ( test17 ) ;
2010-12-21 21:49:49 +03:00
DO_TEST ( test18 ) ;
2011-03-23 01:22:37 +03:00
DO_TEST ( test19 ) ;
2012-06-01 01:50:07 +04:00
DO_TEST ( test20 ) ;
2013-01-16 21:55:06 +04:00
DO_TEST ( test21 ) ;
2014-02-19 05:06:50 +04:00
DO_TEST ( test22 ) ;
2014-02-20 07:04:40 +04:00
DO_TEST ( test23 ) ;
2014-07-15 19:07:02 +04:00
DO_TEST ( test24 ) ;
2010-05-25 15:14:06 +04:00
2013-01-16 14:58:00 +04:00
virMutexLock ( & test - > lock ) ;
if ( test - > running ) {
test - > quit = true ;
/* HACK: Add a dummy timeout to break event loop */
timer = virEventAddTimeout ( 0 , virCommandTestFreeTimer , NULL , NULL ) ;
}
virMutexUnlock ( & test - > lock ) ;
2014-03-25 10:53:44 +04:00
cleanup :
2013-01-16 14:58:00 +04:00
if ( test - > running )
virThreadJoin ( & test - > thread ) ;
if ( timer ! = - 1 )
virEventRemoveTimeout ( timer ) ;
virMutexDestroy ( & test - > lock ) ;
VIR_FREE ( test ) ;
2014-03-17 13:38:38 +04:00
return ret = = 0 ? EXIT_SUCCESS : EXIT_FAILURE ;
2010-05-25 15:14:06 +04:00
}
VIRT_TEST_MAIN ( mymain )
2011-07-28 19:48:12 +04:00
# endif /* !WIN32 */