return (uint64_t)tv.tv_sec * 1000 + ( tv.tv_nsec / 1000000L );
}
-static void parse_cfg( const char *confpath ) {
+static int parse_cfg( const char *confpath ) {
FILE *fp;
char buf[256];
- if ( NULL == confpath || NULL == ( fp = fopen( confpath, "r" ) ) )
- return;
+ if ( NULL == confpath )
+ return EINVAL;
+ if ( NULL == ( fp = fopen( confpath, "r" ) ) )
+ return errno;
while ( fgets( buf, sizeof buf - 1, fp ) ) {
char *key, *val, *x;
key = buf;
cfg.bwlup = str_to_bwl( val );
}
fclose( fp );
+ return 0;
}
static size_t write_memory_cb( void *contents, size_t size, size_t nmemb, void *userp ) {
#define RESP_MISMATCH "Partfile initial sequence mismatch"
static int upload_chunk( CURL *curl, struct file_info_t *fi, off_t csize ) {
- int res = -1;
+ int n, res = -1;
long http_code;
ssize_t br;
char url[2048];
}
if ( csize > br )
csize = br;
- snprintf( url, sizeof url, "%s?fname=%s&fpos=%lu&fsize=%lu", cfg.url, fi->esc_name, fi->coff, fi->size );
-
+ n = snprintf( url, sizeof url, "%s?fname=%s&fpos=%lu&fsize=%lu",
+ cfg.url, fi->esc_name, fi->coff, fi->size );
+ if ( sizeof url <= (size_t)n ) {
+ if ( 0 > n )
+ err( "snprintf(url, ...)", errno );
+ else
+ err( "snprintf(url, ...): URL too long", 0 );
+ goto err_out;
+ }
curl_easy_reset( curl );
error = curl_easy_setopt( curl, CURLOPT_POST, 1L );
error |= curl_easy_setopt( curl, CURLOPT_POSTFIELDSIZE, (long)csize );
goto err_out;
}
- curl_easy_getinfo( curl, CURLINFO_SPEED_UPLOAD_T, &fi->speed );
- curl_easy_getinfo( curl, CURLINFO_RESPONSE_CODE, &http_code );
fi->sent += csize;
+ error = curl_easy_getinfo( curl, CURLINFO_SPEED_UPLOAD_T, &fi->speed );
+ error |= curl_easy_getinfo( curl, CURLINFO_RESPONSE_CODE, &http_code );
+ if ( CURLE_OK != error ) {
+ cerr( "curl_easy_getinfo", error );
+ goto err_out;
+ }
// puts(reply.memory);
res = 0;
}
else
- fprintf( stderr, "ERROR: unexpected response: %s", reply.memory );
+ fprintf( stderr, "\rERROR: unexpected response: %ld %.80s\n", http_code, reply.memory );
break;
case 201:
// upon receiving the first chunk, remote detected a partfile initial
fi->remn -= csize;
res = 0;
}
+ else
+ fprintf( stderr, "\rERROR: unexpected response: %ld %.80s\n", http_code, reply.memory );
break;
case 428:
// remote already has data up to Skip-to-position
res = 0;
}
else
- err( "response code 428 w/o 'Skip-to-position'", 0 );
+ fprintf( stderr, "\rERROR: unexpected response: %ld %.80s\n", http_code, reply.memory );
break;
default:
- fprintf( stderr, "REMOTE ERROR: %ld %s", http_code,
- 100 > reply.size ? reply.memory : ( 403 == http_code ? "Forbidden" : "" ) );
+ fprintf( stderr, "\rREMOTE ERROR: %ld %.80s\n", http_code,
+ 403 == http_code ? "Forbidden" : reply.memory );
break;
}
csize = 2 * fi.speed;
if ( CSIZE_HARDLIMIT < csize )
csize = CSIZE_HARDLIMIT;
- fprintf( stderr, "\r%3.f%% (%5.1f kB/s) ", (double)fi.coff * 100 / fi.size, (double)fi.speed / 1024 );
+ fprintf( stderr, "\r%3.f%% (%5.1f KiB/s) ", (double)fi.coff * 100 / fi.size, (double)fi.speed / 1024 );
// fprintf( stderr, "[csize: %ld] {offset: %ld} ", (long)csize, (long)fi.coff );
fflush( stderr );
}
xfrtime = time_ms() - starttime + 1;
- printf( "\rSent %.1f kB in %.1f seconds (%.1f kB/s).\n",
+ printf( "\rSent %.1f KiB in %.1f seconds (%.1f KiB/s).\n",
(double)fi.sent / 1024, (double)xfrtime / 1000, (double)fi.sent / xfrtime * 1000 / 1024 );
if ( res < 0 )
err( "file upload incomplete", 0 );
return res;
}
-static void usage( char *pname ) {
+static void usage( char *pname, int ec ) {
printf( "%s - upload files to Kaotan\n", basename( pname ) );
printf( "USAGE: %s [OPTIONS] FILE ...\n", basename( pname ) );
printf( "OPTIONS:\n"
" -h display this help text and exit\n"
- " -c file specify configuration file (default: ~/%s)\n"
- " -l maxbps upload speed limit in bytes per second, e.g. 100k\n"
+ " -c file specify configuration file; default: ~/%s\n"
+ " -l maxbps upload speed limit in bytes per second, e.g. 100k; default: 0 (== no limit)\n"
" -u user set user name\n"
" -p passwd set password\n"
- " -U URL set base URL\n"
- , CFG_BASE
+ " -U URL set base URL (default: %s)\n"
+ , CFG_BASE, URL_BASE
);
- printf( "Defaults for bandwidth limit, user, password and URL are taken "
- "from environment and configuration file, in this order.\n" );
- doExit( EXIT_FAILURE );
+ printf(
+ "\nDefaults for bandwidth limit ($BWLUP), user ($USER), password ($PASS) and base URL ($URL)\n"
+ "are taken from the environment and the configuration file, in this order. Values set on\n"
+ "the command line always take precedence over default values.\n"
+ );
+ doExit( ec );
}
int main( int argc, char *argv[] ) {
int res = 0;
int c;
- char confpath[256];
+ char confpath[PATH_MAX];
+
+ signal( SIGINT, sig_handler );
+ signal( SIGTERM, sig_handler );
+ signal( SIGHUP, sig_handler );
cfg.user = strdup( getenv( "USER" ) ? getenv( "USER" ) : "" );
cfg.pass = strdup( getenv( "PASS" ) ? getenv( "PASS" ) : "" );
cfg.url = strdup( getenv( "URL" ) ? getenv( "URL" ) : URL_BASE );
cfg.bwlup = str_to_bwl( getenv( "BWLUP" ) ? getenv( "BWLUP" ) : "0" );
snprintf( confpath, sizeof confpath, "%s/%s", getenv( "HOME" ), CFG_BASE );
- while ( ( c = getopt( argc, argv, "+:c:" ) ) != -1 ) {
+ while ( ( c = getopt( argc, argv, "+:c:h" ) ) != -1 ) {
switch (c) {
case 'c':
- snprintf( confpath, sizeof confpath, "%s", optarg );
+ res = snprintf( confpath, sizeof confpath, "%s", optarg );
+ if ( sizeof confpath <= (size_t)res )
+ die( optarg, res < 0 ? errno : ENAMETOOLONG );
+ break;
+ case 'h':
+ usage( argv[0], EXIT_SUCCESS );
break;
case ':':
fprintf( stderr, "ERROR: missing argument for option '-%c'\n", optopt );
- usage( argv[0] );
+ usage( argv[0], EXIT_FAILURE );
break;
default:
break;
}
}
- parse_cfg( confpath );
+ if ( 0 != ( res = parse_cfg( confpath ) ) )
+ err( confpath, res );
optind = 0;
while ( ( c = getopt( argc, argv, "+:c:hl:p:u:U:" ) ) != -1 ) {
switch (c) {
case 'c':
- // already handled above
- break;
case 'h':
- usage( argv[0] );
+ // already handled above
break;
case 'l':
cfg.bwlup = str_to_bwl( optarg );
break;
case ':':
fprintf( stderr, "ERROR: missing argument for option '-%c'\n", optopt );
- usage( argv[0] );
+ usage( argv[0], EXIT_FAILURE );
break;
case '?':
fprintf( stderr, "ERROR: unknown option '-%c'\n", optopt );
- usage( argv[0] );
+ usage( argv[0], EXIT_FAILURE );
break;
default:
- fprintf( stderr, "ERROR: option '-%c' not implemented, programmer PEBKAC.\n", c );
+ fprintf( stderr, "WARNING: option '-%c' not implemented, programmer PEBKAC.\n", c );
break;
}
}
if ( optind >= argc ) {
err( "no file to upload", 0 );
- usage( argv[0] );
+ usage( argv[0], EXIT_FAILURE );
}
if ( CURLE_OK != curl_global_init( CURL_GLOBAL_SSL ) )
die( "curl_global_init", 0 );
- signal( SIGINT, sig_handler );
- signal( SIGTERM, sig_handler );
- signal( SIGHUP, sig_handler );
-
+ res = 0;
while ( optind < argc && !gotsig && 0 == res )
res = upload_file( argv[optind++] );