2006-01-31 09:09:18 +03:00
/*
CIFSDD - dd for SMB .
Main program , argument handling and block copying .
Copyright ( C ) James Peach 2005 - 2006
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
2007-07-10 06:07:03 +04:00
the Free Software Foundation ; either version 3 of the License , or
2006-01-31 09:09:18 +03:00
( 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
2007-07-10 06:07:03 +04:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2006-01-31 09:09:18 +03:00
*/
# include "includes.h"
# include "system/filesys.h"
# include "auth/gensec/gensec.h"
# include "lib/cmdline/popt_common.h"
2008-01-03 03:39:01 +03:00
# include "libcli/resolve/resolve.h"
2008-01-04 02:22:12 +03:00
# include "libcli/raw/libcliraw.h"
2008-04-22 01:58:23 +04:00
# include "lib/events/events.h"
2006-01-31 09:09:18 +03:00
# include "cifsdd.h"
2007-09-08 16:42:09 +04:00
# include "param/param.h"
2006-01-31 09:09:18 +03:00
const char * const PROGNAME = " cifsdd " ;
# define SYNTAX_EXIT_CODE 1 /* Invokation syntax or logic error. */
# define EOM_EXIT_CODE 9 /* Out of memory error. */
# define FILESYS_EXIT_CODE 10 /* File manipulation error. */
# define IOERROR_EXIT_CODE 11 /* Error during IO phase. */
struct dd_stats_record dd_stats ;
static int dd_sigint ;
static int dd_sigusr1 ;
static void dd_handle_signal ( int sig )
{
switch ( sig )
{
case SIGINT :
+ + dd_sigint ;
break ;
case SIGUSR1 :
+ + dd_sigusr1 ;
break ;
default :
break ;
}
}
/* ------------------------------------------------------------------------- */
/* Argument handling. */
/* ------------------------------------------------------------------------- */
static const char * argtype_str ( enum argtype arg_type )
{
2007-12-20 02:02:15 +03:00
static const struct {
2006-01-31 09:09:18 +03:00
enum argtype arg_type ;
const char * arg_name ;
} names [ ] =
{
{ ARG_NUMERIC , " COUNT " } ,
{ ARG_SIZE , " SIZE " } ,
{ ARG_PATHNAME , " FILE " } ,
{ ARG_BOOL , " BOOLEAN " } ,
} ;
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( names ) ; + + i ) {
if ( arg_type = = names [ i ] . arg_type ) {
return ( names [ i ] . arg_name ) ;
}
}
return ( " <unknown> " ) ;
}
static struct argdef args [ ] =
{
{ " bs " , ARG_SIZE , " force ibs and obs to SIZE bytes " } ,
{ " ibs " , ARG_SIZE , " read SIZE bytes at a time " } ,
{ " obs " , ARG_SIZE , " write SIZE bytes at a time " } ,
{ " count " , ARG_NUMERIC , " copy COUNT input blocks " } ,
{ " seek " , ARG_NUMERIC , " skip COUNT blocks at start of output " } ,
{ " skip " , ARG_NUMERIC , " skip COUNT blocks at start of input " } ,
{ " if " , ARG_PATHNAME , " read input from FILE " } ,
{ " of " , ARG_PATHNAME , " write output to FILE " } ,
{ " direct " , ARG_BOOL , " use direct I/O if non-zero " } ,
{ " sync " , ARG_BOOL , " use synchronous writes if non-zero " } ,
{ " oplock " , ARG_BOOL , " take oplocks on the input and output files " } ,
/* FIXME: We should support using iflags and oflags for setting oplock and I/O
* options . This would make us compatible with GNU dd .
*/
} ;
2007-09-08 20:46:30 +04:00
static struct argdef * find_named_arg ( const char * arg )
2006-01-31 09:09:18 +03:00
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( args ) ; + + i ) {
if ( strwicmp ( arg , args [ i ] . arg_name ) = = 0 ) {
return ( & args [ i ] ) ;
}
}
return ( NULL ) ;
}
int set_arg_argv ( const char * argv )
{
struct argdef * arg ;
char * name ;
char * val ;
if ( ( name = strdup ( argv ) ) = = NULL ) {
return ( 0 ) ;
}
if ( ( val = strchr ( name , ' = ' ) ) = = NULL ) {
fprintf ( stderr , " %s: malformed argument \" %s \" \n " ,
PROGNAME , argv ) ;
goto fail ;
}
* val = ' \0 ' ;
val + + ;
if ( ( arg = find_named_arg ( name ) ) = = NULL ) {
fprintf ( stderr , " %s: ignoring unknown argument \" %s \" \n " ,
PROGNAME , name ) ;
goto fail ;
}
/* Found a matching name; convert the variable argument. */
switch ( arg - > arg_type ) {
case ARG_NUMERIC :
if ( ! conv_str_u64 ( val , & arg - > arg_val . nval ) ) {
goto fail ;
}
break ;
case ARG_SIZE :
if ( ! conv_str_size ( val , & arg - > arg_val . nval ) ) {
goto fail ;
}
break ;
case ARG_BOOL :
if ( ! conv_str_bool ( val , & arg - > arg_val . bval ) ) {
goto fail ;
}
break ;
case ARG_PATHNAME :
if ( ! ( arg - > arg_val . pval = strdup ( val ) ) ) {
goto fail ;
}
break ;
default :
fprintf ( stderr , " %s: argument \" %s \" is of "
" unknown type \n " , PROGNAME , name ) ;
goto fail ;
}
free ( name ) ;
return ( 1 ) ;
fail :
free ( name ) ;
return ( 0 ) ;
}
void set_arg_val ( const char * name , . . . )
{
va_list ap ;
struct argdef * arg ;
va_start ( ap , name ) ;
if ( ( arg = find_named_arg ( name ) ) = = NULL ) {
goto fail ;
}
/* Found a matching name; convert the variable argument. */
switch ( arg - > arg_type ) {
case ARG_NUMERIC :
case ARG_SIZE :
arg - > arg_val . nval = va_arg ( ap , uint64_t ) ;
break ;
case ARG_BOOL :
2006-04-01 03:28:18 +04:00
arg - > arg_val . bval = va_arg ( ap , int ) ;
2006-01-31 09:09:18 +03:00
break ;
case ARG_PATHNAME :
arg - > arg_val . pval = va_arg ( ap , char * ) ;
if ( arg - > arg_val . pval ) {
arg - > arg_val . pval = strdup ( arg - > arg_val . pval ) ;
}
break ;
default :
fprintf ( stderr , " %s: argument \" %s \" is of "
" unknown type \n " , PROGNAME , name ) ;
goto fail ;
}
va_end ( ap ) ;
return ;
fail :
fprintf ( stderr , " %s: ignoring unknown argument \" %s \" \n " ,
PROGNAME , name ) ;
va_end ( ap ) ;
return ;
}
2007-10-07 02:07:21 +04:00
bool check_arg_bool ( const char * name )
2006-01-31 09:09:18 +03:00
{
struct argdef * arg ;
if ( ( arg = find_named_arg ( name ) ) & &
( arg - > arg_type = = ARG_BOOL ) ) {
return ( arg - > arg_val . bval ) ;
}
DEBUG ( 0 , ( " invalid argument name: %s " , name ) ) ;
SMB_ASSERT ( 0 ) ;
2007-10-07 02:07:21 +04:00
return ( false ) ;
2006-01-31 09:09:18 +03:00
}
uint64_t check_arg_numeric ( const char * name )
{
struct argdef * arg ;
if ( ( arg = find_named_arg ( name ) ) & &
( arg - > arg_type = = ARG_NUMERIC | | arg - > arg_type = = ARG_SIZE ) ) {
return ( arg - > arg_val . nval ) ;
}
DEBUG ( 0 , ( " invalid argument name: %s " , name ) ) ;
SMB_ASSERT ( 0 ) ;
return ( - 1 ) ;
}
const char * check_arg_pathname ( const char * name )
{
struct argdef * arg ;
if ( ( arg = find_named_arg ( name ) ) & &
( arg - > arg_type = = ARG_PATHNAME ) ) {
return ( arg - > arg_val . pval ) ;
}
DEBUG ( 0 , ( " invalid argument name: %s " , name ) ) ;
SMB_ASSERT ( 0 ) ;
return ( NULL ) ;
}
static void dump_args ( void )
{
int i ;
DEBUG ( 10 , ( " dumping argument values: \n " ) ) ;
for ( i = 0 ; i < ARRAY_SIZE ( args ) ; + + i ) {
switch ( args [ i ] . arg_type ) {
case ARG_NUMERIC :
case ARG_SIZE :
DEBUG ( 10 , ( " \t %s=%llu \n " , args [ i ] . arg_name ,
( unsigned long long ) args [ i ] . arg_val . nval ) ) ;
break ;
case ARG_BOOL :
DEBUG ( 10 , ( " \t %s=%s \n " , args [ i ] . arg_name ,
args [ i ] . arg_val . bval ? " yes " : " no " ) ) ;
break ;
case ARG_PATHNAME :
DEBUG ( 10 , ( " \t %s=%s \n " , args [ i ] . arg_name ,
args [ i ] . arg_val . pval ?
args [ i ] . arg_val . pval :
" (NULL) " ) ) ;
break ;
default :
SMB_ASSERT ( 0 ) ;
}
}
}
static void cifsdd_help_message ( poptContext pctx ,
enum poptCallbackReason preason ,
struct poptOption * poption ,
const char * parg ,
void * pdata )
{
2007-12-20 02:02:15 +03:00
static const char notes [ ] =
2006-01-31 09:09:18 +03:00
" FILE can be a local filename or a UNC path of the form //server/share/path. \n " ;
char prefix [ 24 ] ;
int i ;
if ( poption - > shortName ! = ' ? ' ) {
poptPrintUsage ( pctx , stdout , 0 ) ;
fprintf ( stdout , " [dd options] \n " ) ;
exit ( 0 ) ;
}
poptPrintHelp ( pctx , stdout , 0 ) ;
fprintf ( stdout , " \n CIFS dd options: \n " ) ;
for ( i = 0 ; i < ARRAY_SIZE ( args ) ; + + i ) {
if ( args [ i ] . arg_name = = NULL ) {
break ;
}
snprintf ( prefix , sizeof ( prefix ) , " %s=%-*s " ,
args [ i ] . arg_name ,
( int ) ( sizeof ( prefix ) - strlen ( args [ i ] . arg_name ) - 2 ) ,
argtype_str ( args [ i ] . arg_type ) ) ;
prefix [ sizeof ( prefix ) - 1 ] = ' \0 ' ;
fprintf ( stdout , " %s%s \n " , prefix , args [ i ] . arg_help ) ;
}
fprintf ( stdout , " \n %s \n " , notes ) ;
exit ( 0 ) ;
}
/* ------------------------------------------------------------------------- */
/* Main block copying routine. */
/* ------------------------------------------------------------------------- */
static void print_transfer_stats ( void )
{
if ( DEBUGLEVEL > 0 ) {
printf ( " %llu+%llu records in (%llu bytes) \n "
" %llu+%llu records out (%llu bytes) \n " ,
( unsigned long long ) dd_stats . in . fblocks ,
( unsigned long long ) dd_stats . in . pblocks ,
( unsigned long long ) dd_stats . in . bytes ,
( unsigned long long ) dd_stats . out . fblocks ,
( unsigned long long ) dd_stats . out . pblocks ,
( unsigned long long ) dd_stats . out . bytes ) ;
} else {
printf ( " %llu+%llu records in \n %llu+%llu records out \n " ,
( unsigned long long ) dd_stats . in . fblocks ,
( unsigned long long ) dd_stats . in . pblocks ,
( unsigned long long ) dd_stats . out . fblocks ,
( unsigned long long ) dd_stats . out . pblocks ) ;
}
}
2008-01-03 03:39:01 +03:00
static struct dd_iohandle * open_file ( struct resolve_context * resolve_ctx ,
2008-12-29 22:24:57 +03:00
struct tevent_context * ev ,
2008-01-04 02:22:12 +03:00
const char * which , const char * * ports ,
2008-09-30 05:07:08 +04:00
struct smbcli_options * smb_options ,
2008-11-02 04:50:22 +03:00
const char * socket_options ,
2008-10-24 15:13:27 +04:00
struct smbcli_session_options * smb_session_options ,
2008-11-02 18:20:00 +03:00
struct gensec_settings * gensec_settings )
2006-01-31 09:09:18 +03:00
{
int options = 0 ;
const char * path = NULL ;
struct dd_iohandle * handle = NULL ;
if ( check_arg_bool ( " direct " ) ) {
options | = DD_DIRECT_IO ;
}
if ( check_arg_bool ( " sync " ) ) {
options | = DD_SYNC_IO ;
}
if ( check_arg_bool ( " oplock " ) ) {
options | = DD_OPLOCK ;
}
if ( strcmp ( which , " if " ) = = 0 ) {
path = check_arg_pathname ( " if " ) ;
2008-04-22 01:58:23 +04:00
handle = dd_open_path ( resolve_ctx , ev , path , ports ,
2008-01-04 02:22:12 +03:00
check_arg_numeric ( " ibs " ) , options ,
2008-11-02 04:50:22 +03:00
socket_options ,
2008-10-24 15:13:27 +04:00
smb_options , smb_session_options ,
2008-11-02 18:20:00 +03:00
gensec_settings ) ;
2006-01-31 09:09:18 +03:00
} else if ( strcmp ( which , " of " ) = = 0 ) {
options | = DD_WRITE ;
path = check_arg_pathname ( " of " ) ;
2008-04-22 01:58:23 +04:00
handle = dd_open_path ( resolve_ctx , ev , path , ports ,
2008-01-04 02:22:12 +03:00
check_arg_numeric ( " obs " ) , options ,
2008-11-02 04:50:22 +03:00
socket_options ,
2008-10-24 15:13:27 +04:00
smb_options , smb_session_options ,
2008-11-02 18:20:00 +03:00
gensec_settings ) ;
2006-01-31 09:09:18 +03:00
} else {
SMB_ASSERT ( 0 ) ;
return ( NULL ) ;
}
if ( ! handle ) {
fprintf ( stderr , " %s: failed to open %s \n " , PROGNAME , path ) ;
}
return ( handle ) ;
}
2008-12-29 22:24:57 +03:00
static int copy_files ( struct tevent_context * ev , struct loadparm_context * lp_ctx )
2006-01-31 09:09:18 +03:00
{
uint8_t * iobuf ; /* IO buffer. */
uint64_t iomax ; /* Size of the IO buffer. */
2006-02-06 07:06:55 +03:00
uint64_t data_size ; /* Amount of data in the IO buffer. */
2006-01-31 09:09:18 +03:00
uint64_t ibs ;
uint64_t obs ;
uint64_t count ;
struct dd_iohandle * ifile ;
struct dd_iohandle * ofile ;
2008-01-04 02:22:12 +03:00
struct smbcli_options options ;
2008-09-30 05:07:08 +04:00
struct smbcli_session_options session_options ;
2008-01-04 02:22:12 +03:00
2006-01-31 09:09:18 +03:00
ibs = check_arg_numeric ( " ibs " ) ;
obs = check_arg_numeric ( " obs " ) ;
count = check_arg_numeric ( " count " ) ;
2010-07-16 08:32:42 +04:00
lpcfg_smbcli_options ( lp_ctx , & options ) ;
lpcfg_smbcli_session_options ( lp_ctx , & session_options ) ;
2008-01-04 02:22:12 +03:00
2006-01-31 09:09:18 +03:00
/* Allocate IO buffer. We need more than the max IO size because we
* could accumulate a remainder if ibs and obs don ' t match .
*/
iomax = 2 * MAX ( ibs , obs ) ;
2007-09-08 20:46:30 +04:00
if ( ( iobuf = malloc_array_p ( uint8_t , iomax ) ) = = NULL ) {
2006-01-31 09:09:18 +03:00
fprintf ( stderr ,
" %s: failed to allocate IO buffer of %llu bytes \n " ,
PROGNAME , ( unsigned long long ) iomax ) ;
return ( EOM_EXIT_CODE ) ;
}
2008-01-04 02:22:12 +03:00
options . max_xmit = MAX ( ibs , obs ) ;
2006-01-31 09:09:18 +03:00
DEBUG ( 4 , ( " IO buffer size is %llu, max xmit is %d \n " ,
2008-01-04 02:22:12 +03:00
( unsigned long long ) iomax , options . max_xmit ) ) ;
2006-01-31 09:09:18 +03:00
2010-07-16 08:32:42 +04:00
if ( ! ( ifile = open_file ( lpcfg_resolve_context ( lp_ctx ) , ev , " if " ,
lpcfg_smb_ports ( lp_ctx ) , & options ,
lpcfg_socket_options ( lp_ctx ) ,
2010-05-09 19:20:01 +04:00
& session_options ,
2010-07-16 08:32:42 +04:00
lpcfg_gensec_settings ( lp_ctx , lp_ctx ) ) ) ) {
2006-01-31 09:09:18 +03:00
return ( FILESYS_EXIT_CODE ) ;
}
2010-07-16 08:32:42 +04:00
if ( ! ( ofile = open_file ( lpcfg_resolve_context ( lp_ctx ) , ev , " of " ,
lpcfg_smb_ports ( lp_ctx ) , & options ,
lpcfg_socket_options ( lp_ctx ) ,
2008-10-24 15:13:27 +04:00
& session_options ,
2010-07-16 08:32:42 +04:00
lpcfg_gensec_settings ( lp_ctx , lp_ctx ) ) ) ) {
2006-01-31 09:09:18 +03:00
return ( FILESYS_EXIT_CODE ) ;
}
/* Seek the files to their respective starting points. */
ifile - > io_seek ( ifile , check_arg_numeric ( " skip " ) * ibs ) ;
ofile - > io_seek ( ofile , check_arg_numeric ( " seek " ) * obs ) ;
2008-01-04 02:22:12 +03:00
DEBUG ( 4 , ( " max xmit was negotiated to be %d \n " , options . max_xmit ) ) ;
2006-01-31 09:09:18 +03:00
2006-02-06 07:06:55 +03:00
for ( data_size = 0 ; ; ) {
2006-01-31 09:09:18 +03:00
/* Handle signals. We are somewhat compatible with GNU dd.
* SIGINT makes us stop , but still print transfer statistics .
* SIGUSR1 makes us print transfer statistics but we continue
* copying .
*/
if ( dd_sigint ) {
break ;
}
if ( dd_sigusr1 ) {
print_transfer_stats ( ) ;
dd_sigusr1 = 0 ;
}
if ( ifile - > io_flags & DD_END_OF_FILE ) {
2006-02-06 07:06:55 +03:00
DEBUG ( 4 , ( " flushing %llu bytes at EOF \n " ,
( unsigned long long ) data_size ) ) ;
while ( data_size > 0 ) {
2006-01-31 09:09:18 +03:00
if ( ! dd_flush_block ( ofile , iobuf ,
2006-02-06 07:06:55 +03:00
& data_size , obs ) ) {
2006-01-31 09:09:18 +03:00
return ( IOERROR_EXIT_CODE ) ;
}
}
goto done ;
}
/* Try and read enough blocks of ibs bytes to be able write
* out one of obs bytes .
*/
2006-02-06 07:06:55 +03:00
if ( ! dd_fill_block ( ifile , iobuf , & data_size , obs , ibs ) ) {
2006-01-31 09:09:18 +03:00
return ( IOERROR_EXIT_CODE ) ;
}
2006-02-06 07:06:55 +03:00
if ( data_size = = 0 ) {
2006-01-31 09:09:18 +03:00
/* Done. */
SMB_ASSERT ( ifile - > io_flags & DD_END_OF_FILE ) ;
}
/* Stop reading when we hit the block count. */
if ( dd_stats . in . bytes > = ( ibs * count ) ) {
ifile - > io_flags | = DD_END_OF_FILE ;
}
/* If we wanted to be a legitimate dd, we would do character
* conversions and other shenanigans here .
*/
/* Flush what we read in units of obs bytes. We want to have
* at least obs bytes in the IO buffer but might not if the
* file is too small .
*/
2006-09-22 19:14:03 +04:00
if ( data_size & &
! dd_flush_block ( ofile , iobuf , & data_size , obs ) ) {
2006-01-31 09:09:18 +03:00
return ( IOERROR_EXIT_CODE ) ;
}
}
done :
print_transfer_stats ( ) ;
return ( 0 ) ;
}
/* ------------------------------------------------------------------------- */
/* Main. */
/* ------------------------------------------------------------------------- */
struct poptOption cifsddHelpOptions [ ] = {
{ NULL , ' \0 ' , POPT_ARG_CALLBACK , ( void * ) & cifsdd_help_message , ' \0 ' , NULL , NULL } ,
{ " help " , ' ? ' , 0 , NULL , ' ? ' , " Show this help message " , NULL } ,
{ " usage " , ' \0 ' , 0 , NULL , ' u ' , " Display brief usage message " , NULL } ,
2006-09-06 16:28:01 +04:00
{ NULL }
2006-01-31 09:09:18 +03:00
} ;
int main ( int argc , const char * * argv )
{
int i ;
const char * * dd_args ;
2008-12-29 22:24:57 +03:00
struct tevent_context * ev ;
2006-01-31 09:09:18 +03:00
poptContext pctx ;
struct poptOption poptions [ ] = {
/* POPT_AUTOHELP */
{ NULL , ' \0 ' , POPT_ARG_INCLUDE_TABLE , cifsddHelpOptions ,
0 , " Help options: " , NULL } ,
POPT_COMMON_SAMBA
POPT_COMMON_CONNECTION
POPT_COMMON_CREDENTIALS
POPT_COMMON_VERSION
2006-09-06 16:28:01 +04:00
{ NULL }
2006-01-31 09:09:18 +03:00
} ;
/* Block sizes. */
set_arg_val ( " bs " , ( uint64_t ) 4096 ) ;
set_arg_val ( " ibs " , ( uint64_t ) 4096 ) ;
set_arg_val ( " obs " , ( uint64_t ) 4096 ) ;
/* Block counts. */
set_arg_val ( " count " , ( uint64_t ) - 1 ) ;
set_arg_val ( " seek " , ( uint64_t ) 0 ) ;
set_arg_val ( " seek " , ( uint64_t ) 0 ) ;
/* Files. */
set_arg_val ( " if " , NULL ) ;
set_arg_val ( " of " , NULL ) ;
/* Options. */
2007-10-07 02:07:21 +04:00
set_arg_val ( " direct " , false ) ;
set_arg_val ( " sync " , false ) ;
set_arg_val ( " oplock " , false ) ;
2006-01-31 09:09:18 +03:00
pctx = poptGetContext ( PROGNAME , argc , argv , poptions , 0 ) ;
while ( ( i = poptGetNextOpt ( pctx ) ) ! = - 1 ) {
;
}
for ( dd_args = poptGetArgs ( pctx ) ; dd_args & & * dd_args ; + + dd_args ) {
if ( ! set_arg_argv ( * dd_args ) ) {
fprintf ( stderr , " %s: invalid option: %s \n " ,
PROGNAME , * dd_args ) ;
exit ( SYNTAX_EXIT_CODE ) ;
}
/* "bs" has the side-effect of setting "ibs" and "obs". */
if ( strncmp ( * dd_args , " bs= " , 3 ) = = 0 ) {
uint64_t bs = check_arg_numeric ( " bs " ) ;
set_arg_val ( " ibs " , bs ) ;
set_arg_val ( " obs " , bs ) ;
}
}
2008-06-14 21:00:53 +04:00
ev = s4_event_context_init ( talloc_autofree_context ( ) ) ;
2008-04-22 01:58:23 +04:00
2007-12-10 06:33:16 +03:00
gensec_init ( cmdline_lp_ctx ) ;
2006-01-31 09:09:18 +03:00
dump_args ( ) ;
if ( check_arg_numeric ( " ibs " ) = = 0 | | check_arg_numeric ( " ibs " ) = = 0 ) {
fprintf ( stderr , " %s: block sizes must be greater that zero \n " ,
PROGNAME ) ;
exit ( SYNTAX_EXIT_CODE ) ;
}
if ( check_arg_pathname ( " if " ) = = NULL ) {
fprintf ( stderr , " %s: missing input filename \n " , PROGNAME ) ;
exit ( SYNTAX_EXIT_CODE ) ;
}
if ( check_arg_pathname ( " of " ) = = NULL ) {
fprintf ( stderr , " %s: missing output filename \n " , PROGNAME ) ;
exit ( SYNTAX_EXIT_CODE ) ;
}
CatchSignal ( SIGINT , dd_handle_signal ) ;
CatchSignal ( SIGUSR1 , dd_handle_signal ) ;
2008-04-22 01:58:23 +04:00
return ( copy_files ( ev , cmdline_lp_ctx ) ) ;
2006-01-31 09:09:18 +03:00
}
/* vim: set sw=8 sts=8 ts=8 tw=79 : */