diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/fuse/connection.c | 194 | ||||
-rw-r--r-- | src/fuse/connection.h | 19 | ||||
-rw-r--r-- | src/fuse/helper.c | 6 | ||||
-rw-r--r-- | src/fuse/helper.h | 12 | ||||
-rw-r--r-- | src/fuse/main.c | 415 | ||||
-rw-r--r-- | src/shared/sockhelper.c | 2 |
6 files changed, 378 insertions, 270 deletions
diff --git a/src/fuse/connection.c b/src/fuse/connection.c index 98b1d36..7bd8018 100644 --- a/src/fuse/connection.c +++ b/src/fuse/connection.c @@ -10,10 +10,12 @@ #include <pthread.h> #include <string.h> #include <stdio.h> +#include <stdatomic.h> #include <unistd.h> #include <errno.h> #include <time.h> #include <inttypes.h> +#include <signal.h> /* Constants */ static const size_t SHORTBUF = 100; @@ -30,9 +32,12 @@ static const int FAIL_BACKOFF_START_COUNT = 8; static bool connectionInitDone = false; static bool threadInitDone = false; static pthread_mutex_t mutexInit = PTHREAD_MUTEX_INITIALIZER; -static bool keepRunning = true; +atomic_bool keepRunning = true; static bool learnNewServers; +static pthread_t tidReceiver; +static pthread_t tidBackground; + // List of pending requests static struct { dnbd3_async_t *head; @@ -58,12 +63,12 @@ static struct { // Known alt servers typedef struct _alt_server { dnbd3_host_t host; - int consecutiveFails; - int rtt; + atomic_int consecutiveFails; + atomic_int rtt; int rtts[RTT_COUNT]; int rttIndex; - int bestCount; - int liveRtt; + atomic_int bestCount; + atomic_int liveRtt; } alt_server_t; static dnbd3_server_entry_t newservers[MAX_ALTS]; @@ -83,20 +88,22 @@ static pthread_rwlock_t altLock = PTHREAD_RWLOCK_INITIALIZER; /* Static methods */ -static void* connection_receiveThreadMain(void *sock); -static void* connection_backgroundThread(void *something); +static void* connection_receiveThreadMain( void *sock ); +static void* connection_backgroundThread( void *something ); static void addAltServers(); static void sortAltServers(); static void probeAltServers(); -static void switchConnection(int sockFd, alt_server_t *srv); +static void switchConnection( int sockFd, alt_server_t *srv ); static void requestAltServers(); -static bool throwDataAway(int sockFd, uint32_t amount); +static bool throwDataAway( int sockFd, uint32_t amount ); + +static void enqueueRequest( dnbd3_async_t *request ); +static dnbd3_async_t* removeRequest( dnbd3_async_t *request ); -static void enqueueRequest(dnbd3_async_t *request); -static dnbd3_async_t* removeRequest(dnbd3_async_t *request); +static void blockSignals(); -bool connection_init(const char *hosts, const char *lowerImage, const uint16_t rid, const bool doLearnNew) +bool connection_init( const char *hosts, const char *lowerImage, const uint16_t rid, const bool doLearnNew ) { int sock = -1; char host[SHORTBUF]; @@ -111,7 +118,7 @@ bool connection_init(const char *hosts, const char *lowerImage, const uint16_t r timing_setBase(); pthread_mutex_lock( &mutexInit ); - if ( !connectionInitDone && keepRunning ) { + if ( !connectionInitDone ) { dnbd3_host_t tempHosts[MAX_HOSTS_PER_ADDRESS]; const char *current, *end; int altIndex = 0; @@ -123,7 +130,7 @@ bool connection_init(const char *hosts, const char *lowerImage, const uint16_t r // Get next host from string while ( *current == ' ' ) current++; end = strchr( current, ' ' ); - size_t len = (end == NULL ? SHORTBUF : (size_t)( end - current ) + 1); + size_t len = ( end == NULL ? SHORTBUF : (size_t)( end - current ) + 1 ); if ( len > SHORTBUF ) len = SHORTBUF; snprintf( host, len, "%s", current ); int newHosts = sock_resolveToDnbd3Host( host, tempHosts, MAX_HOSTS_PER_ADDRESS ); @@ -155,14 +162,14 @@ bool connection_init(const char *hosts, const char *lowerImage, const uint16_t r } if ( sock == -2 || sock == -1 ) continue; - salen = sizeof(sa); + salen = sizeof( sa ); if ( getpeername( sock, (struct sockaddr*)&sa, &salen ) == -1 ) { logadd( LOG_ERROR, "getpeername on successful connection failed!? (errno=%d)", errno ); close( sock ); sock = -1; continue; } - hlen = sock_printable( (struct sockaddr*)&sa, salen, host, sizeof(host) ); + hlen = sock_printable( (struct sockaddr*)&sa, salen, host, sizeof( host ) ); logadd( LOG_INFO, "Connected to %.*s", (int)hlen, host ); if ( !dnbd3_select_image( sock, lowerImage, rid, 0 ) ) { logadd( LOG_ERROR, "Could not send select image" ); @@ -207,12 +214,11 @@ bool connection_init(const char *hosts, const char *lowerImage, const uint16_t r bool connection_initThreads() { pthread_mutex_lock( &mutexInit ); - if ( !keepRunning || !connectionInitDone || threadInitDone || connection.sockFd == -1 ) { + if ( !connectionInitDone || threadInitDone || connection.sockFd == -1 ) { pthread_mutex_unlock( &mutexInit ); return false; } bool success = true; - pthread_t thread; threadInitDone = true; logadd( LOG_DEBUG1, "Initializing stuff" ); if ( pthread_mutex_init( &connection.sendMutex, NULL ) != 0 @@ -220,10 +226,10 @@ bool connection_initThreads() logadd( LOG_ERROR, "Mutex or spinlock init failure" ); success = false; } else { - if ( pthread_create( &thread, NULL, &connection_receiveThreadMain, (void*)(size_t)connection.sockFd ) != 0 ) { + if ( pthread_create( &tidReceiver, NULL, &connection_receiveThreadMain, ( void* )(size_t)connection.sockFd ) != 0 ) { logadd( LOG_ERROR, "Could not create receive thread" ); success = false; - } else if ( pthread_create( &thread, NULL, &connection_backgroundThread, NULL ) != 0 ) { + } else if ( pthread_create( &tidBackground, NULL, &connection_backgroundThread, NULL ) != 0 ) { logadd( LOG_ERROR, "Could not create background thread" ); success = false; } @@ -241,7 +247,7 @@ uint64_t connection_getImageSize() return image.size; } -bool connection_read(dnbd3_async_t *request) +bool connection_read( dnbd3_async_t *request ) { if ( !connectionInitDone ) return false; pthread_mutex_lock( &connection.sendMutex ); @@ -250,9 +256,7 @@ bool connection_read(dnbd3_async_t *request) if ( !dnbd3_get_block( connection.sockFd, request->offset, request->length, (uint64_t)request, 0 ) ) { shutdown( connection.sockFd, SHUT_RDWR ); connection.sockFd = -1; - pthread_mutex_unlock( &connection.sendMutex ); signal_call( connection.panicSignal ); - return true; } } pthread_mutex_unlock( &connection.sendMutex ); @@ -261,24 +265,36 @@ bool connection_read(dnbd3_async_t *request) void connection_close() { - if ( keepRunning ) { - logadd( LOG_INFO, "Tearing down dnbd3 connections and workers" ); - } + static bool signalled = false; + logadd( LOG_INFO, "Tearing down dnbd3 connections and workers" ); pthread_mutex_lock( &mutexInit ); keepRunning = false; + if ( threadInitDone && !signalled ) { + signalled = true; + pthread_kill( tidReceiver, SIGHUP ); + pthread_kill( tidBackground, SIGHUP ); + } + pthread_mutex_unlock( &mutexInit ); if ( !connectionInitDone ) { - pthread_mutex_unlock( &mutexInit ); return; } - pthread_mutex_unlock( &mutexInit ); pthread_mutex_lock( &connection.sendMutex ); if ( connection.sockFd != -1 ) { + logadd( LOG_DEBUG1, "Shutting down socket..." ); shutdown( connection.sockFd, SHUT_RDWR ); } pthread_mutex_unlock( &connection.sendMutex ); } -size_t connection_printStats(char *buffer, const size_t len) +void connection_join() +{ + if ( !threadInitDone ) + return; + pthread_join( tidReceiver, NULL ); + pthread_join( tidBackground, NULL ); +} + +size_t connection_printStats( char *buffer, const size_t len ) { int ret; size_t remaining = len; @@ -308,7 +324,7 @@ size_t connection_printStats(char *buffer, const size_t len) *buffer++ = ' '; } const size_t addrlen = sock_printHost( &altservers[i].host, buffer, remaining ); - remaining -= (addrlen + 1); // For space or * above + remaining -= ( addrlen + 1 ); // For space or * above buffer += addrlen; if ( remaining < 3 ) break; @@ -324,7 +340,7 @@ size_t connection_printStats(char *buffer, const size_t len) width += 3; } ret = snprintf( buffer, remaining, "% *d %s Unreachable:% 5d BestCount:% 5d Live:% 5dµs\n", - width, value, unit, altservers[i].consecutiveFails, altservers[i].bestCount, altservers[i].liveRtt ); + width, value, unit, altservers[i].consecutiveFails, altservers[i].bestCount, altservers[i].liveRtt ); if ( ret < 0 ) { ret = 0; } @@ -339,23 +355,23 @@ size_t connection_printStats(char *buffer, const size_t len) return len - remaining; } -static void* connection_receiveThreadMain(void *sockPtr) +static void* connection_receiveThreadMain( void *sockPtr ) { int sockFd = (int)(size_t)sockPtr; dnbd3_reply_t reply; - pthread_detach( pthread_self() ); + blockSignals(); while ( keepRunning ) { int ret; do { ret = dnbd3_read_reply( sockFd, &reply, true ); + if ( !keepRunning ) goto fail; if ( ret == REPLY_OK ) break; } while ( ret == REPLY_INTR || ret == REPLY_AGAIN ); if ( ret != REPLY_OK ) { logadd( LOG_DEBUG1, "Error receiving reply on receiveThread (%d)", ret ); goto fail; } - if ( reply.cmd == CMD_GET_BLOCK ) { // Get block reply. find matching request dnbd3_async_t *request = removeRequest( (dnbd3_async_t*)reply.handle ); @@ -390,10 +406,8 @@ static void* connection_receiveThreadMain(void *sockPtr) } unlock_rw( &altLock ); } - // Success, wake up caller - request->success = true; - request->finished = true; - signal_call( request->signal ); + fuse_reply_buf( request->fuse_req, request->buffer, request->length ); + free( request ); } } else if ( reply.cmd == CMD_GET_SERVERS ) { // List of known alt servers @@ -416,7 +430,6 @@ static void* connection_receiveThreadMain(void *sockPtr) } } } - logadd( LOG_DEBUG1, "Aus der Schleife rausgeflogen! ARRRRRRRRRR" ); fail:; // Make sure noone is trying to use the socket for sending by locking, pthread_mutex_lock( &connection.sendMutex ); @@ -424,7 +437,9 @@ fail:; // as someone could have established a new connection already if ( connection.sockFd == sockFd ) { connection.sockFd = -1; - signal_call( connection.panicSignal ); + if ( keepRunning ) { + signal_call( connection.panicSignal ); + } } pthread_mutex_unlock( &connection.sendMutex ); // As we're the only reader, it's safe to close the socket now @@ -432,11 +447,12 @@ fail:; return NULL; } -static void* connection_backgroundThread(void *something UNUSED) +static void* connection_backgroundThread( void *something UNUSED ) { ticks nextKeepalive; ticks nextRttCheck; + blockSignals(); timing_get( &nextKeepalive ); nextRttCheck = nextKeepalive; while ( keepRunning ) { @@ -446,6 +462,8 @@ static void* connection_backgroundThread(void *something UNUSED) uint32_t wt2 = timing_diffMs( &now, &nextRttCheck ); if ( wt1 > 0 && wt2 > 0 ) { int waitRes = signal_wait( connection.panicSignal, (int)MIN( wt1, wt2 ) + 1 ); + if ( !keepRunning ) + break; if ( waitRes == SIGNAL_ERROR ) { logadd( LOG_WARNING, "Error waiting on signal in background thread! Errno = %d", errno ); } @@ -470,10 +488,10 @@ static void* connection_backgroundThread(void *something UNUSED) if ( timing_reachedPrecise( &nextKeepalive, &now ) ) { pthread_mutex_lock( &connection.sendMutex ); if ( connection.sockFd != -1 ) { - dnbd3_request_t request; - request.magic = dnbd3_packet_magic; - request.cmd = CMD_KEEPALIVE; - request.handle = request.offset = request.size = 0; + dnbd3_request_t request = { + .magic = dnbd3_packet_magic, + .cmd = CMD_KEEPALIVE, + }; fixup_request( request ); ssize_t ret = sock_sendAll( connection.sockFd, &request, sizeof request, 2 ); if ( (size_t)ret != sizeof request ) { @@ -528,9 +546,10 @@ static void addAltServers() altservers[slot].host = newservers[nIdx].host; altservers[slot].liveRtt = 0; } -skip_server:; +skip_server: + ; } - memset( newservers, 0, sizeof(newservers) ); + memset( newservers, 0, sizeof( newservers ) ); unlock_rw( &altLock ); pthread_mutex_unlock( &newAltLock ); } @@ -604,7 +623,7 @@ static void probeAltServers() pthread_spin_lock( &requests.lock ); if ( requests.head != NULL ) { if ( !panic && current != NULL ) { - const int maxDelay = MAX( current->rtt * 5, 1000000 ); // Give at least one second + const uint64_t maxDelay = MAX( current->rtt * 5, 1000000 ); // Give at least one second dnbd3_async_t *iterator; for ( iterator = requests.head; iterator != NULL; iterator = iterator->next ) { // A request with measurement tag is pending @@ -626,7 +645,7 @@ static void probeAltServers() } lock_read( &altLock ); - for ( int altIndex = 0; altIndex < (panic ? MAX_ALTS : MAX_ALTS_ACTIVE); ++altIndex ) { + for ( int altIndex = 0; altIndex < ( panic ? MAX_ALTS : MAX_ALTS_ACTIVE ); ++altIndex ) { alt_server_t * const srv = &altservers[altIndex]; if ( srv->host.type == 0 ) continue; @@ -640,59 +659,60 @@ static void probeAltServers() srv->rttIndex += 1; } // Probe + char hstr[100]; + sock_printHost( &srv->host, hstr, 100 ); ticks start; timing_get( &start ); errno = 0; int sock = sock_connect( &srv->host, panic ? 1000 : 333, 1000 ); if ( sock == -1 ) { - logadd( LOG_DEBUG1, "Could not connect for probing. errno = %d", errno ); + logadd( LOG_DEBUG1, "%s probe: Could not connect for probing. errno = %d", hstr, errno ); goto fail; } if ( !dnbd3_select_image( sock, image.name, image.rid, 0 ) ) { - logadd( LOG_DEBUG1, "probe: select_image failed" ); + logadd( LOG_DEBUG1, "%s probe: select_image failed (sock=%d, errno=%d)", hstr, sock, errno ); goto fail; } - if ( !dnbd3_select_image_reply( &buffer, sock, &remoteProto, &remoteName, &remoteRid, &remoteSize )) { - logadd( LOG_DEBUG1, "probe: select image reply failed" ); + if ( !dnbd3_select_image_reply( &buffer, sock, &remoteProto, &remoteName, &remoteRid, &remoteSize ) ) { + logadd( LOG_DEBUG1, "%s probe: select image reply failed", hstr ); goto fail; } if ( remoteProto < MIN_SUPPORTED_SERVER ) { - logadd( LOG_WARNING, "Unsupported remote version (local: %d, remote: %d)", (int)PROTOCOL_VERSION, (int)remoteProto ); + logadd( LOG_WARNING, "%s probe: Unsupported remote version (local: %d, remote: %d)", hstr, (int)PROTOCOL_VERSION, (int)remoteProto ); srv->consecutiveFails += 10; goto fail; } if ( remoteRid != image.rid || strcmp( remoteName, image.name ) != 0 ) { - logadd( LOG_WARNING, "Remote rid or name mismatch (got '%s')", remoteName ); + logadd( LOG_WARNING, "%s probe: Remote rid or name mismatch (got '%s')", hstr, remoteName ); srv->consecutiveFails += 10; goto fail; } if ( !dnbd3_get_block( sock, testOffset, testLength, 0, 0 ) ) { - logadd( LOG_DEBUG1, "-> block request fail" ); + logadd( LOG_DEBUG1, "%s probe: -> block request fail", hstr ); goto fail; } int a = 111; - if ( !(a = dnbd3_get_reply( sock, &reply )) || reply.size != testLength ) { - logadd( LOG_DEBUG1, "<- get block reply fail %d %d", a, (int)reply.size ); + if ( !( a = dnbd3_get_reply( sock, &reply ) ) || reply.size != testLength ) { + logadd( LOG_DEBUG1, "%s probe: <- get block reply fail %d %d", hstr, a, (int)reply.size ); goto fail; } if ( request != NULL && removeRequest( request ) != NULL ) { // Request successfully removed from queue const ssize_t ret = sock_recv( sock, request->buffer, request->length ); if ( ret != (ssize_t)request->length ) { - logadd( LOG_DEBUG1, "[RTT] receiving payload for a block reply failed" ); + logadd( LOG_DEBUG1, "%s probe: receiving payload for a block reply failed", hstr ); // Failure, add to queue again connection_read( request ); goto fail; } - // Success, wake up caller - logadd( LOG_DEBUG1, "[RTT] Successful direct probe" ); - request->success = true; - request->finished = true; - signal_call( request->signal ); + // Success, reply to fuse + fuse_reply_buf( request->fuse_req, request->buffer, request->length ); + free( request ); + logadd( LOG_DEBUG1, "%s probe: Successful direct probe", hstr ); } else { // Wasn't a request that's in our request queue if ( !throwDataAway( sock, testLength ) ) { - logadd( LOG_DEBUG1, "<- get block reply payload fail" ); + logadd( LOG_DEBUG1, "%s probe: <- get block reply payload fail", hstr ); goto fail; } } @@ -701,7 +721,7 @@ static void probeAltServers() // Panic mode? Just switch to server if ( panic ) { unlock_rw( &altLock ); - switchConnection( sock, srv ); + if ( keepRunning ) switchConnection( sock, srv ); return; } // Non-panic mode: @@ -733,7 +753,8 @@ static void probeAltServers() close( sock ); } continue; -fail:; +fail: + ; if ( sock != -1 ) { close( sock ); } @@ -774,7 +795,7 @@ fail:; // Regular logic: Apply threshold when considering switch if ( !doSwitch && current != NULL ) { doSwitch = current->rtt > best->rtt + RTT_ABSOLUTE_THRESHOLD - || RTT_THRESHOLD_FACTOR(current->rtt) > best->rtt + 1000; + || RTT_THRESHOLD_FACTOR( current->rtt ) > best->rtt + 1000; } } // Switch if a better server was found @@ -796,11 +817,10 @@ fail:; } } -static void switchConnection(int sockFd, alt_server_t *srv) +static void switchConnection( int sockFd, alt_server_t *srv ) { - pthread_t thread; struct sockaddr_storage addr; - socklen_t addrLen = sizeof(addr); + socklen_t addrLen = sizeof( addr ); char message[200] = "Connection switched to "; const size_t len = strlen( message ); int ret; @@ -829,9 +849,10 @@ static void switchConnection(int sockFd, alt_server_t *srv) signal_call( connection.panicSignal ); return; } + pthread_detach( tidReceiver ); timing_get( &connection.startupTime ); - pthread_create( &thread, NULL, &connection_receiveThreadMain, (void*)(size_t)sockFd ); - sock_printable( (struct sockaddr*)&addr, sizeof(addr), message + len, sizeof(message) - len ); + pthread_create( &tidReceiver, NULL, &connection_receiveThreadMain, ( void* )(size_t)sockFd ); + sock_printable( (struct sockaddr*)&addr, sizeof( addr ), message + len, sizeof( message ) - len ); logadd( LOG_INFO, "%s", message ); // resend queue if ( queue != NULL ) { @@ -863,14 +884,14 @@ static void requestAltServers() request.magic = dnbd3_packet_magic; request.cmd = CMD_GET_SERVERS; fixup_request( request ); - if ( sock_sendAll( connection.sockFd, &request, sizeof(request), 2 ) != (ssize_t)sizeof(request) ) { + if ( sock_sendAll( connection.sockFd, &request, sizeof( request ), 2 ) != (ssize_t)sizeof( request ) ) { logadd( LOG_WARNING, "Connection failed while requesting alt server list" ); shutdown( connection.sockFd, SHUT_RDWR ); connection.sockFd = -1; } } -static bool throwDataAway(int sockFd, uint32_t amount) +static bool throwDataAway( int sockFd, uint32_t amount ) { size_t done = 0; char tempBuffer[SHORTBUF]; @@ -883,11 +904,9 @@ static bool throwDataAway(int sockFd, uint32_t amount) return true; } -static void enqueueRequest(dnbd3_async_t *request) +static void enqueueRequest( dnbd3_async_t *request ) { request->next = NULL; - request->finished = false; - request->success = false; //logadd( LOG_DEBUG2, "Queue: %p @ %s : %d", request, file, line ); // Measure latency and add to switch formula timing_get( &request->time ); @@ -901,7 +920,7 @@ static void enqueueRequest(dnbd3_async_t *request) pthread_spin_unlock( &requests.lock ); } -static dnbd3_async_t* removeRequest(dnbd3_async_t *request) +static dnbd3_async_t* removeRequest( dnbd3_async_t *request ) { pthread_spin_lock( &requests.lock ); //logadd( LOG_DEBUG2, "Remov: %p @ %s : %d", request, file, line ); @@ -925,3 +944,20 @@ static dnbd3_async_t* removeRequest(dnbd3_async_t *request) return iterator; } +static void blockSignals() +{ + sigset_t sigmask; + if ( pthread_sigmask( 0, NULL, &sigmask ) == -1 ) { + logadd( LOG_WARNING, "Cannot get current sigmask of thread" ); + sigemptyset( &sigmask ); + } + sigaddset( &sigmask, SIGUSR1 ); + sigaddset( &sigmask, SIGUSR2 ); + sigaddset( &sigmask, SIGPIPE ); + sigaddset( &sigmask, SIGINT ); + sigaddset( &sigmask, SIGTERM ); + sigdelset( &sigmask, SIGHUP ); + if ( pthread_sigmask( SIG_SETMASK, &sigmask, NULL ) == -1 ) { + logadd( LOG_WARNING, "Cannot set sigmask of thread" ); + } +} diff --git a/src/fuse/connection.h b/src/fuse/connection.h index cae554c..be0115e 100644 --- a/src/fuse/connection.h +++ b/src/fuse/connection.h @@ -3,33 +3,38 @@ #include "../shared/fdsignal.h" #include "../shared/timing.h" +#include <stdatomic.h> #include <stddef.h> #include <stdbool.h> #include <stdint.h> +#define FUSE_USE_VERSION 30 +#include <fuse_lowlevel.h> + +extern atomic_bool keepRunning; struct _dnbd3_async; typedef struct _dnbd3_async { struct _dnbd3_async *next; // Next in this linked list (provate field, not set by caller) - dnbd3_signal_t* signal; // Used to signal the caller - char* buffer; // Caller-provided buffer to be filled ticks time; // When request was put on wire, 0 if not measuring uint64_t offset; uint32_t length; - bool finished; // Will be set to true if the request has been handled - bool success; // Will be set to true if the request succeeded + fuse_req_t fuse_req; + char buffer[]; // Must be last member! } dnbd3_async_t; -bool connection_init(const char *hosts, const char *image, const uint16_t rid, const bool learnNewServers); +bool connection_init( const char *hosts, const char *image, const uint16_t rid, const bool learnNewServers ); bool connection_initThreads(); uint64_t connection_getImageSize(); -bool connection_read(dnbd3_async_t *request); +bool connection_read( dnbd3_async_t *request ); void connection_close(); -size_t connection_printStats(char *buffer, const size_t len); +void connection_join(); + +size_t connection_printStats( char *buffer, const size_t len ); #endif /* CONNECTION_H_ */ diff --git a/src/fuse/helper.c b/src/fuse/helper.c index d81b08f..f54073b 100644 --- a/src/fuse/helper.c +++ b/src/fuse/helper.c @@ -18,8 +18,8 @@ void printLog( log_info *info ) } //rewind(file); - fprintf( logFile, "ImageSize: %"PRIu64" MiB\n", ( uint64_t )( info->imageSize/ ( 1024ll*1024ll ) ) ); - fprintf( logFile, "ReceivedMiB: %"PRIu64" MiB\n", ( uint64_t )( info->receivedBytes/ ( 1024ll*1024ll ) ) ); + fprintf( logFile, "ImageSize: %"PRIu64" MiB\n", (uint64_t)( info->imageSize/ ( 1024ll*1024ll ) ) ); + fprintf( logFile, "ReceivedMiB: %"PRIu64" MiB\n", (uint64_t)( info->receivedBytes/ ( 1024ll*1024ll ) ) ); fprintf( logFile, "imageBlockCount: %"PRIu64"\n", info->imageBlockCount ); fprintf( logFile, "Blocksize: 4KiB\n\n" ); fprintf( logFile, "Block access count:\n" ); @@ -29,7 +29,7 @@ void printLog( log_info *info ) if ( i % 50 == 0 ) { fprintf( logFile, "\n" ); } - fprintf( logFile, "%i ", ( int ) info->blockRequestCount[i] ); + fprintf( logFile, "%i ", (int) info->blockRequestCount[i] ); } fprintf( logFile, "\n" ); fclose( logFile ); diff --git a/src/fuse/helper.h b/src/fuse/helper.h index 9e5d127..65cca2c 100644 --- a/src/fuse/helper.h +++ b/src/fuse/helper.h @@ -18,18 +18,18 @@ typedef struct log_info { -void printLog(log_info *info); +void printLog( log_info *info ); -int connect_to_server(char *server_adress, int port); +int connect_to_server( char *server_adress, int port ); -static inline bool isSameAddressPort(const dnbd3_host_t * const a, const dnbd3_host_t * const b) +static inline bool isSameAddressPort( const dnbd3_host_t * const a, const dnbd3_host_t * const b ) { - return (a->type == b->type) && (a->port == b->port) && (0 == memcmp( a->addr, b->addr, (a->type == HOST_IP4 ? 4 : 16) )); + return ( a->type == b->type ) && ( a->port == b->port ) && ( 0 == memcmp( a->addr, b->addr, ( a->type == HOST_IP4 ? 4 : 16 ) ) ); } -static inline bool isSameAddress(const dnbd3_host_t * const a, const dnbd3_host_t * const b) +static inline bool isSameAddress( const dnbd3_host_t * const a, const dnbd3_host_t * const b ) { - return (a->type == b->type) && (0 == memcmp( a->addr, b->addr, (a->type == HOST_IP4 ? 4 : 16) )); + return ( a->type == b->type ) && ( 0 == memcmp( a->addr, b->addr, ( a->type == HOST_IP4 ? 4 : 16 ) ) ); } #endif diff --git a/src/fuse/main.c b/src/fuse/main.c index 1a5643c..f303c9c 100644 --- a/src/fuse/main.c +++ b/src/fuse/main.c @@ -5,6 +5,7 @@ * See the file COPYING. * * Changed by Stephan Schwaer + * FUSE lowlevel by Alan Reichert * */ #include "connection.h" @@ -13,10 +14,15 @@ #include "../shared/log.h" #define FUSE_USE_VERSION 30 -#include <fuse.h> +#include "../config.h" +#include <fuse_lowlevel.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <assert.h> /* for printing uint */ #define __STDC_FORMAT_MACROS #include <inttypes.h> @@ -27,8 +33,14 @@ #define debugf(...) do { logadd( LOG_DEBUG1, __VA_ARGS__ ); } while (0) -static const char * const IMAGE_PATH = "/img"; -static const char * const STATS_PATH = "/status"; +#define INO_ROOT (1) +#define INO_STATS (2) +#define INO_IMAGE (3) + +static const char *IMAGE_NAME = "img"; +static const char *STATS_NAME = "status"; + +static struct fuse_session *_fuseSession = NULL; static uint64_t imageSize; /* Debug/Benchmark variables */ @@ -36,222 +48,224 @@ static bool useDebug = false; static log_info logInfo; static struct timespec startupTime; static uid_t owner; -static bool keepRunning = true; -static void (*fuse_sigIntHandler)(int) = NULL; -static void (*fuse_sigTermHandler)(int) = NULL; -static struct fuse_operations dnbd3_fuse_no_operations; - -#define SIGPOOLSIZE 6 -static pthread_spinlock_t sigLock; -static dnbd3_signal_t *signalPool[SIGPOOLSIZE]; -static dnbd3_signal_t **sigEnd = signalPool + SIGPOOLSIZE; -static void signalInit() -{ - pthread_spin_init( &sigLock, PTHREAD_PROCESS_PRIVATE ); - for ( size_t i = 0; i < SIGPOOLSIZE; ++i ) { - signalPool[i] = NULL; - } -} -static inline dnbd3_signal_t *signalGet() -{ - pthread_spin_lock( &sigLock ); - for ( dnbd3_signal_t **it = signalPool; it < sigEnd; ++it ) { - if ( *it != NULL ) { - dnbd3_signal_t *ret = *it; - *it = NULL; - pthread_spin_unlock( &sigLock ); - return ret; - } - } - pthread_spin_unlock( &sigLock ); - return signal_newBlocking(); -} -static inline void signalPut(dnbd3_signal_t *signal) -{ - pthread_spin_lock( &sigLock ); - for ( dnbd3_signal_t **it = signalPool; it < sigEnd; ++it ) { - if ( *it == NULL ) { - *it = signal; - pthread_spin_unlock( &sigLock ); - return; - } - } - pthread_spin_unlock( &sigLock ); - signal_close( signal ); -} -static int image_getattr(const char *path, struct stat *stbuf) +static int reply_buf_limited( fuse_req_t req, const char *buf, size_t bufsize, off_t off, size_t maxsize ); +static void fillStatsFile( fuse_req_t req, size_t size, off_t offset ); +static void image_destroy( void *private_data ); +static void image_ll_getattr( fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi ); +static void image_ll_init( void *userdata, struct fuse_conn_info *conn ); +static void image_ll_lookup( fuse_req_t req, fuse_ino_t parent, const char *name ); +static void image_ll_open( fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi ); +static void image_ll_readdir( fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi ); +static void image_ll_read( fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, struct fuse_file_info *fi ); +static int image_stat( fuse_ino_t ino, struct stat *stbuf ); +static void printUsage( char *argv0, int exitCode ); +static void printVersion(); + +static int image_stat( fuse_ino_t ino, struct stat *stbuf ) { - int res = 0; - memset( stbuf, 0, sizeof( struct stat ) ); - stbuf->st_ctim = stbuf->st_atim = stbuf->st_mtim = startupTime; - stbuf->st_uid = owner; - if ( strcmp( path, "/" ) == 0 ) { + switch ( ino ) { + case INO_ROOT: stbuf->st_mode = S_IFDIR | 0550; stbuf->st_nlink = 2; - } else if ( strcmp( path, IMAGE_PATH ) == 0 ) { + stbuf->st_mtim = startupTime; + break; + case INO_IMAGE: stbuf->st_mode = S_IFREG | 0440; stbuf->st_nlink = 1; stbuf->st_size = imageSize; - } else if ( strcmp( path, STATS_PATH ) == 0 ) { + stbuf->st_mtim = startupTime; + break; + case INO_STATS: stbuf->st_mode = S_IFREG | 0440; stbuf->st_nlink = 1; stbuf->st_size = 4096; clock_gettime( CLOCK_REALTIME, &stbuf->st_mtim ); + break; + default: + return -1; + } + stbuf->st_ctim = stbuf->st_atim = startupTime; + stbuf->st_uid = owner; + stbuf->st_ino = ino; + return 0; +} + +static void image_ll_getattr( fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi ) +{ + struct stat stbuf = { 0 }; + ( void ) fi; + + if ( image_stat( ino, &stbuf ) == -1 ) { + fuse_reply_err( req, ENOENT ); } else { - res = -ENOENT; + fuse_reply_attr( req, &stbuf, ino == INO_IMAGE ? 1200 : 1 ); // seconds validity timeout } - return res; } -static int image_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset UNUSED, struct fuse_file_info *fi UNUSED) +static void image_ll_lookup( fuse_req_t req, fuse_ino_t parent, const char *name ) { - if ( strcmp( path, "/" ) != 0 ) { - return -ENOENT; + ( void )parent; + + if ( strcmp( name, IMAGE_NAME ) == 0 || strcmp( name, STATS_NAME ) == 0 ) { + struct fuse_entry_param e = { 0 }; + if ( strcmp( name, IMAGE_NAME ) == 0 ) { + e.ino = INO_IMAGE; + e.attr_timeout = e.entry_timeout = 1200; + } else { + e.ino = INO_STATS; + e.attr_timeout = e.entry_timeout = 0; + } + if ( image_stat( e.ino, &e.attr ) == 0 ) { + fuse_reply_entry( req, &e ); + return; + } } - filler( buf, ".", NULL, 0 ); - filler( buf, "..", NULL, 0 ); - filler( buf, IMAGE_PATH + 1, NULL, 0 ); - filler( buf, STATS_PATH + 1, NULL, 0 ); - return 0; + fuse_reply_err( req, ENOENT ); +} + +struct dirbuf { + char *p; + size_t size; +}; + +static void dirbuf_add( fuse_req_t req, struct dirbuf *b, const char *name, fuse_ino_t ino ) +{ + struct stat stbuf = { .st_ino = ino }; + size_t oldsize = b->size; + b->size += fuse_add_direntry( req, NULL, 0, name, NULL, 0 ); + b->p = ( char * ) realloc( b->p, b->size ); + fuse_add_direntry( req, b->p + oldsize, b->size - oldsize, name, &stbuf, b->size ); + return; } -static int image_open(const char *path, struct fuse_file_info *fi) +static int reply_buf_limited( fuse_req_t req, const char *buf, size_t bufsize, off_t off, size_t maxsize ) { - if ( strcmp( path, IMAGE_PATH ) != 0 && strcmp( path, STATS_PATH ) != 0 ) { - return -ENOENT; + if ( off >= 0 && off < (off_t)bufsize ) { + return fuse_reply_buf( req, buf + off, MIN( bufsize - off, maxsize ) ); } - if ( ( fi->flags & 3 ) != O_RDONLY ) { - return -EACCES; + return fuse_reply_buf( req, NULL, 0 ); +} + +static void image_ll_readdir( fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi ) +{ + ( void ) fi; + + if ( ino != INO_ROOT ) { + fuse_reply_err( req, ENOTDIR ); + } else { + struct dirbuf b; + memset( &b, 0, sizeof( b ) ); + dirbuf_add( req, &b, ".", INO_ROOT ); + dirbuf_add( req, &b, "..", INO_ROOT ); + dirbuf_add( req, &b, IMAGE_NAME, INO_IMAGE ); + dirbuf_add( req, &b, STATS_NAME, INO_STATS ); + reply_buf_limited( req, b.p, b.size, off, size ); + free( b.p ); } - return 0; } -static int fillStatsFile(char *buf, size_t size, off_t offset) { - if ( offset == 0 ) { - return (int)connection_printStats( buf, size ); +static void image_ll_open( fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi ) +{ + if ( ino != INO_IMAGE && ino != INO_STATS ) { + fuse_reply_err( req, EISDIR ); + } else if ( ( fi->flags & 3 ) != O_RDONLY ) { + fuse_reply_err( req, EACCES ); + } else { + // auto caching + fi->keep_cache = 1; + fuse_reply_open( req, fi ); } +} + +static void fillStatsFile( fuse_req_t req, size_t size, off_t offset ) { char buffer[4096]; int ret = (int)connection_printStats( buffer, sizeof buffer ); int len = MIN( ret - (int)offset, (int)size ); - if ( len == 0 ) - return 0; if ( len < 0 ) { - return -EOF; + fuse_reply_err( req, 0 ); + return; } - memcpy( buf, buffer + offset, len ); - return len; + fuse_reply_buf( req, buffer + offset, len ); } -static int image_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi UNUSED) +static void image_ll_read( fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, struct fuse_file_info *fi ) { - if ( size > __INT_MAX__ ) { - // fuse docs say we MUST fill the buffer with exactly size bytes and return size, - // otherwise the buffer will we padded with zeros. Since the return value is just - // an int, we could not properly fulfill read requests > 2GB. Since there is no - // mention of a guarantee that this will never happen, better add a safety check. - // Way to go fuse. - return -EIO; - } - if ( path[1] == STATS_PATH[1] ) { - return fillStatsFile( buf, size, offset ); + assert( ino == INO_STATS || ino == INO_IMAGE ); + + ( void )fi; + + if ( ino == INO_STATS ) { + fillStatsFile( req, size, offset ); + return; } if ( (uint64_t)offset >= imageSize ) { - return 0; + fuse_reply_err( req, 0 ); + return; } - if ( offset + size > imageSize ) { size = imageSize - offset; } + if ( size == 0 || size > UINT32_MAX ) { + fuse_reply_err( req, 0 ); + return; + } if ( useDebug ) { - /* count the requested blocks */ uint64_t startBlock = offset / ( 4096 ); const uint64_t endBlock = ( offset + size - 1 ) / ( 4096 ); - for ( ; startBlock <= endBlock; startBlock++ ) { + for ( ; startBlock <= endBlock; startBlock++ ) + { ++logInfo.blockRequestCount[startBlock]; } } - - dnbd3_async_t request; - request.buffer = buf; - request.length = (uint32_t)size; - request.offset = offset; - request.signal = signalGet(); - - if ( !connection_read( &request ) ) { - signalPut( request.signal ); - return -EINVAL; - } - while ( !request.finished ) { - int ret = signal_wait( request.signal, 5000 ); - if ( !keepRunning ) { - connection_close(); - break; - } - if ( ret < 0 ) { - debugf( "fuse_read signal wait returned %d", ret ); - } - } - signalPut( request.signal ); - if ( request.success ) { - return request.length; - } else { - return -EIO; + dnbd3_async_t *request = malloc( sizeof(dnbd3_async_t) + size ); + request->length = (uint32_t)size; + request->offset = offset; + request->fuse_req = req; + + if ( !connection_read( request ) ) { + fuse_reply_err( req, EIO ); + free( request ); } } -static void image_sigHandler(int signum) { - keepRunning = false; - if ( signum == SIGINT && fuse_sigIntHandler != NULL ) { - fuse_sigIntHandler(signum); - } - if ( signum == SIGTERM && fuse_sigTermHandler != NULL ) { - fuse_sigTermHandler(signum); - } +static void noopSigHandler( int signum ) +{ + (void)signum; } -static void* image_init(struct fuse_conn_info *conn UNUSED) +static void image_ll_init( void *userdata, struct fuse_conn_info *conn ) { + ( void ) userdata; + ( void ) conn; if ( !connection_initThreads() ) { logadd( LOG_ERROR, "Could not initialize threads for dnbd3 connection, exiting..." ); - exit( EXIT_FAILURE ); + if ( _fuseSession != NULL ) { + fuse_session_exit( _fuseSession ); + } } - // Prepare our handler - struct sigaction newHandler; - memset( &newHandler, 0, sizeof(newHandler) ); - newHandler.sa_handler = &image_sigHandler; - sigemptyset( &newHandler.sa_mask ); - struct sigaction oldHandler; - // Retrieve old handlers when setting - sigaction( SIGINT, &newHandler, &oldHandler ); - fuse_sigIntHandler = oldHandler.sa_handler; - logadd( LOG_DEBUG1, "Previous SIGINT handler was %p", (void*)(uintptr_t)fuse_sigIntHandler ); - sigaction( SIGTERM, &newHandler, &oldHandler ); - fuse_sigTermHandler = oldHandler.sa_handler; - logadd( LOG_DEBUG1, "Previous SIGTERM handler was %p", (void*)(uintptr_t)fuse_sigIntHandler ); - return NULL; } /* close the connection */ -static void image_destroy(void *private_data UNUSED) +static void image_destroy( void *private_data UNUSED ) { if ( useDebug ) { printLog( &logInfo ); } connection_close(); - return; } /* map the implemented fuse operations */ -static struct fuse_operations image_oper = { - .getattr = image_getattr, - .readdir = image_readdir, - .open = image_open, - .read = image_read, - .init = image_init, +static struct fuse_lowlevel_ops image_oper = { + .lookup = image_ll_lookup, + .getattr = image_ll_getattr, + .readdir = image_ll_readdir, + .open = image_ll_open, + .read = image_ll_read, + .init = image_ll_init, .destroy = image_destroy, }; @@ -259,14 +273,16 @@ static void printVersion() { char *arg[] = { "foo", "-V" }; printf( "DNBD3-Fuse Version 1.2.3.4, protocol version %d\n", (int)PROTOCOL_VERSION ); - fuse_main( 2, arg, &dnbd3_fuse_no_operations, NULL ); + struct fuse_args args = FUSE_ARGS_INIT( 2, arg ); + fuse_parse_cmdline( &args, NULL, NULL, NULL ); exit( 0 ); } -static void printUsage(char *argv0, int exitCode) +static void printUsage( char *argv0, int exitCode ) { char *arg[] = { argv0, "-h" }; - fuse_main( 2, arg, &dnbd3_fuse_no_operations, NULL ); + struct fuse_args args = FUSE_ARGS_INIT( 2, arg ); + fuse_parse_cmdline( &args, NULL, NULL, NULL ); printf( "\n" ); printf( "Usage: %s [--debug] [--option mountOpts] --host <serverAddress(es)> --image <imageName> [--rid revision] <mountPoint>\n", argv0 ); printf( "Or: %s [-d] [-o mountOpts] -h <serverAddress(es)> -i <imageName> [-r revision] <mountPoint>\n", argv0 ); @@ -284,19 +300,19 @@ static void printUsage(char *argv0, int exitCode) static const char *optString = "dfHh:i:l:o:r:SsVv"; static const struct option longOpts[] = { - { "debug", no_argument, NULL, 'd' }, - { "help", no_argument, NULL, 'H' }, - { "host", required_argument, NULL, 'h' }, - { "image", required_argument, NULL, 'i' }, - { "log", required_argument, NULL, 'l' }, - { "option", required_argument, NULL, 'o' }, - { "rid", required_argument, NULL, 'r' }, - { "sticky", no_argument, NULL, 'S' }, - { "version", no_argument, NULL, 'v' }, - { 0, 0, 0, 0 } + { "debug", no_argument, NULL, 'd' }, + { "help", no_argument, NULL, 'H' }, + { "host", required_argument, NULL, 'h' }, + { "image", required_argument, NULL, 'i' }, + { "log", required_argument, NULL, 'l' }, + { "option", required_argument, NULL, 'o' }, + { "rid", required_argument, NULL, 'r' }, + { "sticky", no_argument, NULL, 'S' }, + { "version", no_argument, NULL, 'v' }, + { 0, 0, 0, 0 } }; -int main(int argc, char *argv[]) +int main( int argc, char *argv[] ) { char *server_address = NULL; char *image_Name = NULL; @@ -306,6 +322,10 @@ int main(int argc, char *argv[]) int newArgc; int opt, lidx; bool learnNewServers = true; + bool single_thread = false; + struct fuse_chan *ch; + char *mountpoint; + int foreground = 0; if ( argc <= 1 || strcmp( argv[1], "--help" ) == 0 || strcmp( argv[1], "--usage" ) == 0 ) { printUsage( argv[0], 0 ); @@ -316,9 +336,10 @@ int main(int argc, char *argv[]) log_setConsoleTimestamps( true ); log_setFileMask( 65535 ); - newArgv = calloc( argc + 10, sizeof(char*) ); + newArgv = calloc( argc + 10, sizeof( char* ) ); newArgv[0] = argv[0]; newArgc = 1; + while ( ( opt = getopt_long( argc, argv, optString, longOpts, &lidx ) ) != -1 ) { switch ( opt ) { case 'h': @@ -328,7 +349,7 @@ int main(int argc, char *argv[]) image_Name = optarg; break; case 'r': - rid = (uint16_t)atoi(optarg); + rid = (uint16_t)atoi( optarg ); break; case 'o': newArgv[newArgc++] = "-o"; @@ -357,15 +378,16 @@ int main(int argc, char *argv[]) case 'd': useDebug = true; newArgv[newArgc++] = "-d"; + foreground = 1; break; case 's': - newArgv[newArgc++] = "-s"; + single_thread = true; break; case 'S': learnNewServers = false; break; case 'f': - newArgv[newArgc++] = "-f"; + foreground = 1; break; default: printUsage( argv[0], EXIT_FAILURE ); @@ -386,6 +408,17 @@ int main(int argc, char *argv[]) } } + // Prepare our handler + struct sigaction newHandler; + memset( &newHandler, 0, sizeof( newHandler ) ); + newHandler.sa_handler = &noopSigHandler; + sigemptyset( &newHandler.sa_mask ); + sigaction( SIGHUP, &newHandler, NULL ); + sigset_t sigmask; + sigemptyset( &sigmask ); + sigaddset( &sigmask, SIGHUP ); + pthread_sigmask( SIG_BLOCK, &sigmask, NULL ); + if ( !connection_init( server_address, image_Name, rid, learnNewServers ) ) { logadd( LOG_ERROR, "Could not connect to any server. Bye.\n" ); return EXIT_FAILURE; @@ -404,17 +437,51 @@ int main(int argc, char *argv[]) // Since dnbd3 is always read only and the remote image will not change newArgv[newArgc++] = "-o"; - newArgv[newArgc++] = "ro,auto_cache,default_permissions"; + newArgv[newArgc++] = "ro,default_permissions"; // Mount point goes last newArgv[newArgc++] = argv[optind]; - printf( "ImagePathName: %s\nFuseArgs:",IMAGE_PATH ); + printf( "ImagePathName: /%s\nFuseArgs:", IMAGE_NAME ); for ( int i = 0; i < newArgc; ++i ) { printf( " '%s'", newArgv[i] ); } - putchar('\n'); + putchar( '\n' ); clock_gettime( CLOCK_REALTIME, &startupTime ); owner = getuid(); - signalInit(); - return fuse_main( newArgc, newArgv, &image_oper, NULL ); + + // Fuse lowlevel loop + struct fuse_args args = FUSE_ARGS_INIT( newArgc, newArgv ); + int fuse_err = 1; + if ( fuse_parse_cmdline( &args, &mountpoint, NULL, NULL ) == -1 ) { + logadd( LOG_ERROR, "FUSE: Parsing command line failed" ); + } else if ( ( ch = fuse_mount( mountpoint, &args ) ) == NULL ) { + logadd( LOG_ERROR, "Mounting file system failed" ); + } else { + _fuseSession = fuse_lowlevel_new( &args, &image_oper, sizeof( image_oper ), NULL ); + if ( _fuseSession == NULL ) { + logadd( LOG_ERROR, "Could not initialize fuse session" ); + } else { + if ( fuse_set_signal_handlers( _fuseSession ) == -1 ) { + logadd( LOG_ERROR, "Could not install fuse signal handlers" ); + } else { + fuse_session_add_chan( _fuseSession, ch ); + fuse_daemonize( foreground ); + if ( single_thread ) { + fuse_err = fuse_session_loop( _fuseSession ); + } else { + fuse_err = fuse_session_loop_mt( _fuseSession ); //MT produces errors (race conditions) in libfuse and didnt improve speed at all + } + fuse_remove_signal_handlers( _fuseSession ); + fuse_session_remove_chan( ch ); + } + fuse_session_destroy( _fuseSession ); + _fuseSession = NULL; + } + fuse_unmount( mountpoint, ch ); + } + fuse_opt_free_args( &args ); + free( newArgv ); + connection_join(); + logadd( LOG_DEBUG1, "Terminating. FUSE REPLIED: %d\n", fuse_err ); + return fuse_err; } diff --git a/src/shared/sockhelper.c b/src/shared/sockhelper.c index 9e9109c..4ff93a6 100644 --- a/src/shared/sockhelper.c +++ b/src/shared/sockhelper.c @@ -61,7 +61,7 @@ int sock_connect(const dnbd3_host_t * const addr, const int connect_ms, const in for ( int i = 0; i < 5; ++i ) { int ret = connect( client_sock, (struct sockaddr *)&ss, addrlen ); e2 = errno; - if ( ret != -1 || errno == EINPROGRESS || errno == EISCONN ) break; + if ( ret != -1 || ( connect_ms == -1 && errno == EINPROGRESS ) || errno == EISCONN ) break; if ( errno == EINTR ) { // http://www.madore.org/~david/computers/connect-intr.html #ifdef __linux__ |