49a856cb5c01903c697f866e922c3723cd0d714b galt Tue Mar 1 21:08:09 2016 -0800 Fixes #16872. bigBedToBed on HTTPS URL was terminating with exitcode 141 and being killed by default SIGPIPE behavior. This now has a fairly elegant solution that should work on most OSes. diff --git src/lib/https.c src/lib/https.c index 1a8f026..754c05c 100644 --- src/lib/https.c +++ src/lib/https.c @@ -1,28 +1,29 @@ /* Connect via https. */ /* Copyright (C) 2012 The Regents of the University of California * See README in this or parent directory for licensing information. */ #ifdef USE_SSL #include "openssl/ssl.h" #include "openssl/err.h" #include #include #include +#include #include "common.h" #include "internet.h" #include "errAbort.h" #include "net.h" static pthread_mutex_t *mutexes = NULL; static unsigned long openssl_id_callback(void) { return ((unsigned long)pthread_self()); } static void openssl_locking_callback(int mode, int n, const char * file, int line) @@ -270,31 +271,31 @@ { /* Timed out - just quit */ xerr("https timeout expired"); goto cleanup; } else { if (FD_ISSET(params->sv[1], &readfds)) { swt = 0; srd = read(params->sv[1], sbuf, 32768); if (srd == -1) { if (errno != 104) // udcCache often closes causing "Connection reset by peer" - xerrno("error reading https socket"); + xerrno("error reading user pipe for https socket"); goto cleanup; } if (srd == 0) break; // user closed socket, we are done } if (FD_ISSET(fd, &writefds)) { int swtx = BIO_write(sbio, sbuf+swt, srd-swt); if (swtx <= 0) { if (!BIO_should_write(sbio)) { ERR_print_errors_fp(stderr); xerr("Error writing SSL connection"); @@ -323,36 +324,62 @@ { brd = 0; continue; } else { if (brd == 0) break; ERR_print_errors_fp(stderr); xerr("Error reading SSL connection"); goto cleanup; } } // write the https data received immediately back on socket to user, and it's ok if it blocks. while(bwt < brd) { + // NOTE: Intended as a thread-specific library-safe way not to ignore SIG_PIPE for the entire application. + // temporarily block sigpipe on this thread + sigset_t sigpipe_mask; + sigemptyset(&sigpipe_mask); + sigaddset(&sigpipe_mask, SIGPIPE); + sigset_t saved_mask; + if (pthread_sigmask(SIG_BLOCK, &sigpipe_mask, &saved_mask) == -1) { + perror("pthread_sigmask"); + exit(1); + } int bwtx = write(params->sv[1], bbuf+bwt, brd-bwt); + int saveErrno = errno; + if ((bwtx == -1) && (saveErrno == EPIPE)) + { // if there was a EPIPE, accept and consume the SIGPIPE now. + struct timespec zerotime = {0}; + if (sigtimedwait(&sigpipe_mask, 0, &zerotime) == -1) + { + perror("sigtimedwait"); + exit(1); + } + } + // restore signal mask on this thread + if (pthread_sigmask(SIG_SETMASK, &saved_mask, 0) == -1) + { + perror("pthread_sigmask"); + exit(1); + } if (bwtx == -1) { - if ((errno != 104) // udcCache often closes causing "Connection reset by peer" - && (errno != 32)) // udcCache often closes causing "Broken pipe" - xerrno("error writing https data back to user socket"); + if ((saveErrno != 104) // udcCache often closes causing "Connection reset by peer" + && (saveErrno != 32)) // udcCache often closes causing "Broken pipe" + xerrno("error writing https data back to user pipe"); goto cleanup; } bwt += bwtx; } brd = 0; bwt = 0; } } } cleanup: BIO_free_all(sbio); close(params->sv[1]); /* we are done with it */ @@ -362,30 +389,34 @@ int netConnectHttps(char *hostName, int port) /* Return socket for https connection with server or -1 if error. */ { fflush(stdin); fflush(stdout); fflush(stderr); struct netConnectHttpsParams *params; AllocVar(params); params->hostName = cloneString(hostName); params->port = port; socketpair(AF_UNIX, SOCK_STREAM, 0, params->sv); +// netBlockBrokenPipes(); works, but is heavy handed +// and ignores SIGPIPE on all connections for all threads in the entire application. +// We are trying something more subtle and library and thread-friendly instead. + int rc; rc = pthread_create(¶ms->thread, NULL, netConnectHttpsThread, (void *)params); if (rc) { errAbort("Unexpected error %d from pthread_create(): %s",rc,strerror(rc)); } /* parent */ return params->sv[0]; } #else