static int initCondAndMutex(void) { int status; pthread_mutexattr_t mutexAttr; status = pthread_mutexattr_init(&mutexAttr); if (status) { LOG_ERRNUM0(status, "Couldn't initialize mutex attributes"); } else { (void)pthread_mutexattr_setprotocol(&mutexAttr, PTHREAD_PRIO_INHERIT); /* * Recursive in case `termSigHandler()` and `waitUntilDone()` execute * on the same thread */ (void)pthread_mutexattr_settype(&mutexAttr, PTHREAD_MUTEX_RECURSIVE); if ((status = pthread_mutex_init(&mutex, &mutexAttr))) { LOG_ERRNUM0(status, "Couldn't initialize mutex"); } else { if ((status = pthread_cond_init(&cond, NULL))) { LOG_ERRNUM0(status, "Couldn't initialize condition variable"); (void)pthread_mutex_destroy(&mutex); } } (void)pthread_mutexattr_destroy(&mutexAttr); } // `mutexAttr` initialized return status; }
/** * Creates a file data-reader and starts it in a new thread. * * @retval 0 Success * @retval 1 Usage failure. \c log_start() called. * @retval 2 O/S failure. \c log_start() called. */ static int spawnFileReader( const pthread_attr_t* const attr, /**< [in] Thread-creation * attributes */ const char* const pathname, /**< [in] Pathname of input file or * NULL to read standard input * stream */ Fifo* const fifo, /**< [in] Pointer to FIFO into * which to put data */ Reader** const reader, /**< [out] Pointer to pointer to * address of reader */ pthread_t* const thread) /**< [out] Pointer to pointer to * created thread */ { Reader* fileReader; int status = fileReaderNew(NULL, fifo, &fileReader); if (0 != status) { LOG_ADD0("Couldn't create file-reader"); } else { pthread_t thrd; if ((status = pthread_create(&thrd, attr, readerStart, fileReader)) != 0) { LOG_ERRNUM0(status, "Couldn't start file-reader thread"); status = 1; } else { *reader = fileReader; *thread = thrd; } } return status; }
/** * Creates a product-maker and starts it in a new thread. * * @retval 0 Success. * @retval 1 Usage failure. \c log_start() called. * @retval 2 O/S failure. \c log_start() called. */ static int spawnProductMaker( const pthread_attr_t* const attr, /**< [in] Thread-creation * attributes */ Fifo* const fifo, /**< [in] Pointer to FIFO from * which to get data */ LdmProductQueue* const productQueue, /**< [in] LDM product-queue into * which to put data-products * */ ProductMaker** const productMaker, /**< [out] Pointer to pointer to * returned product-maker */ pthread_t* const thread) /**< [out] Pointer to pointer * to created thread */ { ProductMaker* pm; int status = pmNew(fifo, productQueue, &pm); if (0 != status) { LOG_ADD0("Couldn't create new LDM product-maker"); status = 1; } else { pthread_t thrd; if (0 != (status = pthread_create(&thrd, attr, pmStart, pm))) { LOG_ERRNUM0(status, "Couldn't start product-maker thread"); status = 1; } else { *productMaker = pm; *thread = thrd; } } return status; }
/** * Initializes a FIFO * * @retval 0 Success * @retval 1 Usage error. `log_start()` called. * @retval 2 O/S failure. `log_start()` called. */ static int fifo_init( Fifo* const fifo, /**< [in/out] Pointer to the FIFO */ unsigned char* const buf, /**< [in] The buffer */ const size_t size) /**< [in] Size of the buffer in bytes */ { int status; pthread_mutexattr_t mutexAttr; if ((status = pthread_mutexattr_init(&mutexAttr)) != 0) { LOG_ERRNUM0(status, "Couldn't initialize mutex attributes"); status = 2; } else { // At most one lock per thread. (void)pthread_mutexattr_settype(&mutexAttr, PTHREAD_MUTEX_ERRORCHECK); // Prevent priority inversion (void)pthread_mutexattr_setprotocol(&mutexAttr, PTHREAD_PRIO_INHERIT); if ((status = pthread_mutex_init(&fifo->mutex, &mutexAttr)) != 0) { LOG_ERRNUM0(status, "Couldn't initialize mutex"); status = 2; } else { if ((status = pthread_cond_init(&fifo->cond, NULL)) != 0) { LOG_ERRNUM0(status, "Couldn't initialize condition variable"); status = 2; } else { fifo->buf = buf; fifo->nextWrite = 0; fifo->nbytes = 0; // indicates startup fifo->size = size; fifo->isClosed = false; fifo->fullCount = 0; status = 0; // success } // `fifo->cond` initialized if (status) (void)pthread_mutex_destroy(&fifo->mutex); } /* "fifo->mutex" initialized */ (void)pthread_mutexattr_destroy(&mutexAttr); } /* "mutexAttr" initialized */ return status; }
static void unlockMutex(void) { udebug("Unlocking mutex"); int status = pthread_mutex_unlock(&mutex); if (status) { LOG_ERRNUM0(status, "Couldn't unlock mutex"); abortProcess(); } }
static void setDoneCondition(void) { lockMutex(); done = 1; udebug("Signaling condition variable"); int status = pthread_cond_broadcast(&cond); if (status) { LOG_ERRNUM0(status, "Couldn't signal condition variable"); abortProcess(); } unlockMutex(); }
static void waitForDoneCondition(void) { lockMutex(); while (!done) { udebug("Waiting on condition variable"); int status = pthread_cond_wait(&cond, &mutex); if (status) { LOG_ERRNUM0(status, "Couldn't wait on condition variable"); abortProcess(); } } unlockMutex(); }
/** * Creates a multicast data-reader and starts it in a new thread. * * @retval 0 Success * @retval 1 Usage failure. \c log_start() called. * @retval 2 O/S failure. \c log_start() called. */ static int spawnMulticastReader( const pthread_attr_t* const attr, /**< [in] Thread-creation * attributes */ const char* const mcastAddr, /**< [in] Dotted-quad * representation of the multicast * group */ const char* const interface, /**< [in] IPv4 address of interface * on which to listen for * multicast UDP packets in IPv4 * dotted-quad format or NULL to * listen on all available * interfaces */ Fifo* const fifo, /**< [in] Pointer to FIFO into * which to put data */ Reader** const reader, /**< [out] Pointer to pointer to * address of reader */ pthread_t* const thread) /**< [out] Pointer to pointer to * created thread */ { Reader* multicastReader; int status = multicastReaderNew(mcastAddr, interface, fifo, &multicastReader); if (0 != status) { LOG_ADD0("Couldn't create multicast-reader"); } else { pthread_t thrd; if (0 != (status = pthread_create(&thrd, attr, readerStart, multicastReader))) { LOG_ERRNUM0(status, "Couldn't start multicast-reader thread"); status = 1; } else { *reader = multicastReader; *thread = thrd; } } return status; }
/** * Returns a new product-maker. * * This function is thread-safe. * * @retval 0 Success. * @retval 1 Usage failure. \c log_start() called. * @retval 2 O/S failure. \c log_start() called. */ int pmNew( Fifo* const fifo, /**< [in] Pointer to FIFO from * which to get data */ LdmProductQueue* const lpq, /**< [in] LDM product-queue into * which to put data-products */ ProductMaker** const productMaker) /**< [out] Pointer to pointer to * returned product-maker */ { int status = 2; /* default failure */ ProductMaker* w = (ProductMaker*)malloc(sizeof(ProductMaker)); if (NULL == w) { LOG_SERROR0("Couldn't allocate new product-maker"); } else { MD5_CTX* md5ctxp = new_MD5_CTX(); if (NULL == md5ctxp) { LOG_SERROR0("Couldn't allocate MD5 object"); } else { if ((status = pthread_mutex_init(&w->mutex, NULL)) != 0) { LOG_ERRNUM0(status, "Couldn't initialize product-maker mutex"); status = 2; } else { w->fifo = fifo; w->ldmProdQueue = lpq; w->npackets = 0; w->nmissed = 0; w->nprods = 0; w->md5ctxp = md5ctxp; w->status = 0; *productMaker = w; } } } return status; }
/** * @retval LDM7_INVAL No multicast sender child process exists. */ static int sender_terminate(void) { int retval = 0; int status; #if CANCEL_SENDER udebug("Canceling sender thread"); status = pthread_cancel(sender.thread); if (status) { LOG_ERRNUM0(status, "Couldn't cancel sender thread"); retval = status; } #else udebug("Writing to termination pipe"); status = write(sender.fds[1], &status, sizeof(int)); if (status == -1) { LOG_SERROR0("Couldn't write to termination pipe"); retval = status; } #endif void* statusPtr; udebug("Joining sender thread"); status = pthread_join(sender.thread, &statusPtr); if (status) { LOG_ERRNUM0(status, "Couldn't join sender thread"); retval = status; } if (statusPtr != PTHREAD_CANCELED) { status = *(int*)statusPtr; if (status) { LOG_START1("Sender task exit-status was %d", status); retval = status; } } (void)close(sender.sock); udebug("Terminating multicast sender"); status = terminateMcastSender(); if (status) { LOG_ADD0("Couldn't terminate multicast sender process"); retval = status; } udebug("Clearing multicast LDM sender manager"); status = mlsm_clear(); if (status) { LOG_ADD0("mlsm_clear() failure"); retval = status; } status = pq_close(pq); if (status) { LOG_ADD0("pq_close() failure"); retval = status; } status = deleteProductQueue(UP7_PQ_PATHNAME); if (status) { LOG_ADD0("deleteProductQueue() failure"); retval = status; } return retval; }
/** * Reads a NOAAPORT data stream, creates LDM data-products from the stream, and * inserts the data-products into an LDM product-queue. The NOAAPORT data * stream can take the form of multicast UDP packets from (for example) a * Novra S300 DVB-S2 receiver or the standard input stream. * * Usage: * noaaportIngester [-l <em>log</em>] [-n|-v|-x] [-q <em>queue</em>] [-u <em>n</em>] [-m <em>mcastAddr</em>] [-I <em>iface</em>] [-b <em>npages</em>]\n * * Where: * <dl> * <dt>-b <em>npages</em></dt> * <dd>Allocate \e npages pages of memory for the internal buffer.</dd> * * <dt>-I <em>iface</em></dt> * <dd>Listen for multicast packets on interface \e iface.</dd> * * <dt>-l <em>log</em></dt> * <dd>Log to file \e log. The default is to use the system logging daemon * if the current process is a daemon; otherwise, the standard error * stream is used.</dd> * * <dt>-m <em>mcastAddr</em></dt> * <dd>Use the multicast address \e mcastAddr. The default is to * read from the standard input stream.</dd> * * <dt>-n</dt> * <dd>Log messages of level NOTE and higher priority. Each data-product * will generate a log message.</dd> * * <dt>-q <em>queue</em></dt> * <dd>Use \e queue as the pathname of the LDM product-queue. The default * is to use the default LDM pathname of the product-queue.</dd> * * <dt>-u <em>n</em></dt> * <dd>If logging is to the system logging daemon, then use facility * <b>local</b><em>n</em>. The default is to use the LDM facility.</dd> * * <dt>-v</dt> * <dd>Log messages of level INFO and higher priority.</dd> * * <dt>-x</dt> * <dd>Log messages of level DEBUG and higher priority.</dd> * </dl> * * If neither -n, -v, nor -x is specified, then logging will be restricted to * levels ERROR and WARN only. * * @retval 0 if successful. * @retval 1 if an error occurred. At least one error-message will be logged. */ int main( const int argc, /**< [in] Number of arguments */ char* const argv[]) /**< [in] Arguments */ { int status = 0; /* default success */ extern int optind; extern int opterr; int ch; const char* const progName = ubasename(argv[0]); const char* interface = NULL; int logmask = LOG_UPTO(LOG_WARNING); const unsigned logOptions = LOG_CONS | LOG_PID; const char* mcastSpec = NULL; const char* prodQueuePath = NULL; size_t npages = DEFAULT_NPAGES; Fifo* fifo; int ttyFd = open("/dev/tty", O_RDONLY); int processPriority = 0; int idx; const char* logPath = (-1 == ttyFd) ? NULL /* log to system logging daemon */ : "-"; /* log to standard error stream */ (void)close(ttyFd); (void)setulogmask(logmask); status = initLogging(progName, logOptions, logFacility, logPath); opterr = 0; /* no error messages from getopt(3) */ while (0 == status && (ch = getopt(argc, argv, "b:I:l:m:np:q:r:s:t:u:vx")) != -1) { switch (ch) { extern char* optarg; extern int optopt; case 'b': { unsigned long n; if (sscanf(optarg, "%lu", &n) != 1) { LOG_SERROR1("Couldn't decode FIFO size in pages: \"%s\"", optarg); status = 1; } else { npages = n; } break; } case 'I': interface = optarg; break; case 'l': logPath = optarg; status = initLogging(progName, logOptions, logFacility, logPath); break; case 'm': mcastSpec = optarg; break; case 'n': logmask |= LOG_MASK(LOG_NOTICE); (void)setulogmask(logmask); break; case 'p': { char* cp; errno = 0; processPriority = (int)strtol(optarg, &cp, 0); if (0 != errno) { LOG_SERROR1("Couldn't decode priority \"%s\"", optarg); log_log(LOG_ERR); } else { if (processPriority < -20) processPriority = -20; else if (processPriority > 20) processPriority = 20; } break; } case 'q': prodQueuePath = optarg; break; case 'r': #ifdef RETRANS_SUPPORT retrans_xmit_enable = atoi(optarg); if(retrans_xmit_enable == 1) retrans_xmit_enable = OPTION_ENABLE; else retrans_xmit_enable = OPTION_DISABLE; #endif break; case 's': { #ifdef RETRANS_SUPPORT strcpy(sbn_channel_name, optarg); if(!strcmp(optarg,NAME_SBN_TYP_GOES)) { sbn_type = SBN_TYP_GOES; break; } if(!strcmp(optarg,NAME_SBN_TYP_NOAAPORT_OPT)) { sbn_type = SBN_TYP_NOAAPORT_OPT; break; } if(!strcmp(optarg,"NWSTG")) { sbn_type = SBN_TYP_NMC; break; } if(!strcmp(optarg,NAME_SBN_TYP_NMC)) { sbn_type = SBN_TYP_NMC; break; } if(!strcmp(optarg,NAME_SBN_TYP_NMC2)) { sbn_type = SBN_TYP_NMC2; break; } if(!strcmp(optarg,NAME_SBN_TYP_NMC3)) { sbn_type = SBN_TYP_NMC3; break; } if(!strcmp(optarg,NAME_SBN_TYP_NWWS)) { sbn_type = SBN_TYP_NWWS; break; } if(!strcmp(optarg,NAME_SBN_TYP_ADD)) { sbn_type = SBN_TYP_ADD; break; } if(!strcmp(optarg,NAME_SBN_TYP_ENC)) { sbn_type = SBN_TYP_ENC; break; } if(!strcmp(optarg,NAME_SBN_TYP_EXP)) { sbn_type = SBN_TYP_EXP; break; } if(!strcmp(optarg,NAME_SBN_TYP_GRW)) { sbn_type = SBN_TYP_GRW; break; } if(!strcmp(optarg,NAME_SBN_TYP_GRE)) { sbn_type = SBN_TYP_GRE; break; } printf("Operator input: UNKNOWN type must be\n"); printf(" %s, %s, %s, %s, %s, %s, %s, %s, %s, %s or %s \n", NAME_SBN_TYP_NMC, NAME_SBN_TYP_GOES, NAME_SBN_TYP_NOAAPORT_OPT, NAME_SBN_TYP_NMC2, NAME_SBN_TYP_NMC3, NAME_SBN_TYP_NWWS, NAME_SBN_TYP_ADD, NAME_SBN_TYP_ENC, NAME_SBN_TYP_EXP, NAME_SBN_TYP_GRW, NAME_SBN_TYP_GRE); #endif break; } case 't': #ifdef RETRANS_SUPPORT strcpy(transfer_type, optarg); if(!strcmp(transfer_type,"MHS") || !strcmp(transfer_type,"mhs")){ /** Using MHS for communication with NCF **/ }else{ uerror("No other mechanism other than MHS is currently supported\n"); status = 1; } #endif break; case 'u': { int i = atoi(optarg); if (0 > i || 7 < i) { LOG_START1("Invalid logging facility number: %d", i); status = 1; } else { static int logFacilities[] = {LOG_LOCAL0, LOG_LOCAL1, LOG_LOCAL2, LOG_LOCAL3, LOG_LOCAL4, LOG_LOCAL5, LOG_LOCAL6, LOG_LOCAL7}; logFacility = logFacilities[i]; status = initLogging(progName, logOptions, logFacility, logPath); } break; } case 'v': logmask |= LOG_MASK(LOG_INFO); (void)setulogmask(logmask); break; case 'x': logmask |= LOG_MASK(LOG_DEBUG); (void)setulogmask(logmask); break; default: optopt = ch; /*FALLTHROUGH*/ /* no break */ case '?': { uerror("Unknown option: \"%c\"", optopt); status = 1; break; } } /* option character switch */ } /* getopt() loop */ if (0 == status) { if (optind < argc) { uerror("Extraneous command-line argument: \"%s\"", argv[optind]); status = 1; } } if (0 != status) { uerror("Error decoding command-line"); usage(progName); } else { unotice("Starting Up %s", PACKAGE_VERSION); unotice("%s", COPYRIGHT_NOTICE); if ((status = fifoNew(npages, &fifo)) != 0) { LOG_ADD0("Couldn't create FIFO"); log_log(LOG_ERR); } else { LdmProductQueue* prodQueue; if ((status = lpqGet(prodQueuePath, &prodQueue)) != 0) { LOG_ADD0("Couldn't open LDM product-queue"); log_log(LOG_ERR); } else { if (NULL == mcastSpec) { if (0 == (status = spawnProductMaker(NULL, fifo, prodQueue, &productMaker, &productMakerThread))) { status = spawnFileReader(NULL, NULL, fifo, &reader, &readerThread); } } /* reading file */ else { pthread_attr_t attr; if (0 != (status = pthread_attr_init(&attr))) { LOG_ERRNUM0(status, "Couldn't initialize thread attribute"); } else { #ifndef _POSIX_THREAD_PRIORITY_SCHEDULING uwarn("Can't adjust thread priorities due to lack of " "necessary support from environment"); #else /* * In order to not miss any data, the reader thread * should preempt the product-maker thread as soon as * data is available and run as long as data is * available. */ const int SCHED_POLICY = SCHED_FIFO; struct sched_param param; param.sched_priority = sched_get_priority_max(SCHED_POLICY) - 1; (void)pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); (void)pthread_attr_setschedpolicy(&attr, SCHED_POLICY); (void)pthread_attr_setschedparam(&attr, ¶m); (void)pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); #endif #ifdef RETRANS_SUPPORT if (retrans_xmit_enable == OPTION_ENABLE){ /* Copy mcastAddress needed to obtain the cpio entries */ strcpy(mcastAddr, mcastSpec); } #endif if (0 == (status = spawnProductMaker(&attr, fifo, prodQueue, &productMaker, &productMakerThread))) { #ifdef _POSIX_THREAD_PRIORITY_SCHEDULING param.sched_priority++; (void)pthread_attr_setschedparam(&attr, ¶m); #endif status = spawnMulticastReader(&attr, mcastSpec, interface, fifo, &reader, &readerThread); } /* product-maker spawned */ } /* "attr" initialized */ } /* reading multicast packets */ if (0 != status) { log_log(LOG_ERR); status = 1; } else { pthread_t statThread; (void)gettimeofday(&startTime, NULL); reportTime = startTime; (void)pthread_create(&statThread, NULL, reportStatsWhenSignaled, NULL); set_sigactions(); (void)pthread_join(readerThread, NULL); status = readerStatus(reader); (void)pthread_cancel(statThread); (void)pthread_join(statThread, NULL); (void)fifoCloseWhenEmpty(fifo); (void)pthread_join(productMakerThread, NULL); if (0 != status) status = pmStatus(productMaker); reportStats(); readerFree(reader); #ifdef RETRANS_SUPPORT /** Release buffer allocated for retransmission **/ if(retrans_xmit_enable == OPTION_ENABLE){ freeRetransMem(); } #endif } /* "reader" spawned */ (void)lpqClose(prodQueue); } /* "prodQueue" open */ } /* "fifo" created */ } /* command line decoded */ return status; }