static int sender_start( const feedtypet feedtype) { // The following ensures that the first product-index will be 0 int status = pim_delete(NULL, feedtype); if (status) { LOG_ADD1("Couldn't delete product-index map for feedtype %#lx", feedtype); } else { status = createEmptyProductQueue(UP7_PQ_PATHNAME); if (status) { LOG_ADD1("Couldn't create empty product queue \"%s\"", UP7_PQ_PATHNAME); } else { // Thread-safe because 2 threads: upstream LDM-7 & product insertion. status = pq_open(getQueuePath(), PQ_READONLY | PQ_THREADSAFE, &pq); if (status) { LOG_ADD1("Couldn't open product-queue \"%s\"", getQueuePath()); } else { McastInfo* mcastInfo; status = setMcastInfo(&mcastInfo, feedtype); if (status) { LOG_ADD0("Couldn't set multicast information"); } else { status = mlsm_clear(); status = mlsm_addPotentialSender(mcastInfo, 2, LOCAL_HOST, UP7_PQ_PATHNAME); if (status) { LOG_ADD0("mlsm_addPotentialSender() failure"); } else { // Starts the sender on a new thread status = sender_spawn(); if (status) { LOG_ADD0("Couldn't spawn sender"); } else { done = 0; } } mi_free(mcastInfo); } // `mcastInfo` allocated if (status) (void)pq_close(pq); } // Product-queue open if (status) (void)deleteProductQueue(UP7_PQ_PATHNAME); } // empty product-queue created } // product-index map deleted return status; }
/** * Indicates whether or not the anti-denial-of-service attack feature is * enabled. * * @retval 0 The feature is disabled. * @retval 1 The feature is enabled. */ int isAntiDosEnabled(void) { static int isEnabled; static int isSet = 0; if (!isSet) { int status = reg_getBool(REG_ANTI_DOS, &isEnabled); if (status) { isEnabled = 1; LOG_ADD1("Using default value: %s", isEnabled ? "TRUE" : "FALSE"); if (status == ENOENT) { log_log(LOG_INFO); isSet = 1; } else { log_log(LOG_ERR); } } else { isSet = 1; } } return isEnabled; }
/** * Locks a read/write lock for writing. Waits until the lock is available. * Reentrant. * * @retval RWL_SUCCESS Success * @retval RWL_INVALID Lock structure is invalid. log_add() called. * @retval RWL_EXIST Lock is locked for reading and the current process is * the one that created it. log_add() called. * @retval RWL_SYSTEM System error. See "errno". log_add() called. Resulting * state of the lock is unspecified. */ srwl_Status srwl_writeLock( srwl_Lock* const lock /**< [in/out] the lock to be locked */) { srwl_Status status = vet(lock); if (RWL_SUCCESS == status) { if (0 < lock->numReadLocks) { LOG_ADD1("Lock is locked for reading; semId=%d", lock->semId); status = RWL_EXIST; } else if (0 < lock->numWriteLocks) { lock->numWriteLocks++; status = RWL_SUCCESS; } else { if (semop(lock->semId, writeLockOps, sizeof(writeLockOps) / sizeof(writeLockOps[0])) == -1) { LOG_SERROR1("Couldn't lock for writing: semId=%d", lock->semId); status = RWL_SYSTEM; } else { lock->numWriteLocks = 1; status = RWL_SUCCESS; } } } return status; }
/** * Returns the backlog time-offset for making requests of an upstream LDM. * * @return The backlog time-offset, in seconds, for making requests of an * upstream LDM. */ unsigned getTimeOffset(void) { static unsigned timeOffset; static int isSet = 0; if (!isSet) { int status = reg_getUint(REG_TIME_OFFSET, &timeOffset); if (status) { timeOffset = 3600; LOG_ADD1("Using default value: %u seconds", timeOffset); if (status == ENOENT) { log_log(LOG_INFO); isSet = 1; } else { log_log(LOG_ERR); } } else { isSet = 1; } } return timeOffset; }
/* * Returns the LDM proxy status for a given client failure. Logs the failure. * * Arguments: * proxy The LDM proxy data-structure. * name The name of the failed message or NULL. * info Metadata on the data-product that couldn't be sent or * NULL. * Returns: * 0 Success. * LP_TIMEDOUT The failure was due to an RPC timeout. "log_start()" * called iff "name" isn't NULL. * LP_RPC_ERROR RPC error. "log_start()" called iff "name" isn't NULL. */ static LdmProxyStatus getStatus( LdmProxy* const proxy, const char* const name, prod_info* const info) { LdmProxyStatus status; struct rpc_err rpcErr; clnt_geterr(proxy->clnt, &rpcErr); if (0 == rpcErr.re_status) { status = 0; } else { if (NULL != name) { LOG_START3("%s failure to host \"%s\": %s", name, proxy->host, clnt_errmsg(proxy->clnt)); if (NULL != info) { LOG_ADD1("Couldn't send product: %s", s_prod_info(NULL, 0, info, ulogIsDebug())); } } status = RPC_TIMEDOUT == rpcErr.re_status ? LP_TIMEDOUT : LP_RPC_ERROR; } return status; }
static void sender_insertProducts(void) { pqueue* pq; int status = pq_open(UP7_PQ_PATHNAME, 0, &pq); CU_ASSERT_EQUAL_FATAL(status, 0); product prod; prod_info* info = &prod.info; char ident[80]; void* data = NULL; unsigned short xsubi[3] = {(unsigned short)1234567890, (unsigned short)9876543210, (unsigned short)1029384756 }; info->feedtype = EXP; info->ident = ident; info->origin = "localhost"; (void)memset(info->signature, 0, sizeof(info->signature)); for (int i = 0; i < NUM_PRODS; i++) { const unsigned size = MAX_PROD_SIZE*erand48(xsubi) + 0.5; const ssize_t nbytes = snprintf(ident, sizeof(ident), "%d", i); CU_ASSERT_TRUE_FATAL(nbytes >= 0 && nbytes < sizeof(ident)); status = set_timestamp(&info->arrival); CU_ASSERT_EQUAL_FATAL(status, 0); info->seqno = i; uint32_t signet = htonl(i); (void)memcpy(info->signature+sizeof(signaturet)-sizeof(signet), &signet, sizeof(signet)); info->sz = size; data = realloc(data, size); CU_ASSERT_PTR_NOT_NULL(data); prod.data = data; status = pq_insert(pq, &prod); CU_ASSERT_EQUAL_FATAL(status, 0); char buf[LDM_INFO_MAX]; LOG_ADD1("Inserted: prodInfo=\"%s\"", s_prod_info(buf, sizeof(buf), info, 1)); log_log(LOG_INFO); struct timespec duration; duration.tv_sec = 0; duration.tv_nsec = 5000000; // 5 ms status = nanosleep(&duration, NULL); CU_ASSERT_EQUAL_FATAL(status, 0); } free(data); status = pq_close(pq); CU_ASSERT_EQUAL_FATAL(status, 0); }
static int createEmptyProductQueue( const char* const pathname) { pqueue* pq; int status = pq_create(pathname, 0666, PQ_DEFAULT, 0, PQ_SIZE, NUM_PQ_SLOTS, &pq); // PQ_DEFAULT => clobber existing if (status == 0) { status = pq_close(pq); if (status) { LOG_ADD1("Couldn't close product-queue \"%s\"", pathname); } } return status; }
/** * Executes a multicast receiver. Blocks until the receiver is stopped. * * @param[in,out] receiver The receiver. * @retval 0 Success. The receiver was stopped. * @retval EINVAL @code{receiver == NULL}. \c log_add() called. * @retval -1 Other failure. \c log_add() called. */ int mcastReceiver_execute( const McastReceiver* const receiver) { if (0 == receiver) { LOG_ADD0("NULL receiver argument"); return EINVAL; } try { receiver->receiver->RunReceivingThread(); return 0; } catch (const std::exception& e) { LOG_ADD1("%s", e.what()); return -1; } }
/** * Executes a multicast receiver. Only returns when an error occurs. * * @param[in,out] receiver The receiver. * @retval EINVAL @code{receiver == NULL}. \c log_add() called. * @retval -1 Other failure. \c log_add() called. */ int mcastReceiver_execute( const McastReceiver* const receiver) { if (0 == receiver) { LOG_ADD0("NULL receiver argument"); return EINVAL; } try { // VCMTP call receiver->receiver->Start(); } catch (const std::exception& e) { LOG_ADD1("%s", e.what()); } return -1; }
/* * Returns the path name of a file. * * Arguments: * name The name of the registry parameter that contains the * desired pathname. * buf Buffer of length PATH_MAX into which to put the path. * desc Pointer to a description of the file. * Returns: * NULL Error. "log_log()" called. * else Pointer to the pathname of the file. Might be absolute * or relative to the current working directory. */ static const char* getPath( const char* const name, char buf[PATH_MAX], const char* const desc) { if (0 == buf[0]) { char* var; if (reg_getString(name, &var)) { LOG_ADD1("Couldn't get pathname of %s", desc); return NULL; } else { setPath(var, buf); free(var); } } return buf; }
/** * Locks a read/write lock for reading. Waits until the lock is available. * Reentrant. * * @retval RWL_SUCCESS Success * @retval RWL_INVALID Lock structure is invalid. log_add() called. * @retval RWL_EXIST Lock is locked for writing and the current process is * the one that created it. log_add() called. * @retval RWL_SYSTEM System error. See "errno". log_add() called. Resulting * state of the lock is unspecified. */ srwl_Status srwl_readLock( srwl_Lock* const lock /**< [in/out] the lock to be locked */) { srwl_Status status = vet(lock); if (RWL_SUCCESS == status) { if (0 < lock->numWriteLocks) { LOG_ADD1("Lock is locked for writing; semId=%d", lock->semId); status = RWL_EXIST; } else if (0 < lock->numReadLocks) { lock->numReadLocks++; status = RWL_SUCCESS; } else { /* * A read-lock is obtained in two steps because the semop(2) * specification doesn't indicate that the operations array is * executed sequentially. */ if (semop(lock->semId, readLockOps, sizeof(readLockOps) / sizeof(readLockOps[0])) == -1) { LOG_SERROR1("Couldn't lock for reading: semId=%d", lock->semId); status = RWL_SYSTEM; } else if (semop(lock->semId, shareOps, sizeof(shareOps) / sizeof(shareOps[0])) == -1) { LOG_SERROR1("Couldn't share read-lock: semId=%d", lock->semId); status = RWL_SYSTEM; } else { lock->numReadLocks = 1; status = RWL_SUCCESS; } } } return status; }
/** * Returns a new server contact information object. * * @param[in] id Name or formatted IP address of the host running the server. * @param[in] port Port number of the server. * @retval NULL Error. \c log_add() called. * @return Pointer to a new server contact information object. The * client should call \c serverInfo_free() when it is no longer * needed. */ ServerInfo* serverInfo_new( const char* const id, const unsigned short port) { ServerInfo* si = LOG_MALLOC(sizeof(ServerInfo), "server contact information"); if (si) { si->id = strdup(id); if (!si->id) { LOG_ADD1("Couldn't duplicate server identifier \"%s\"", id); free(si); si = NULL; } else { si->port = port; } } return si; }
/** * Feeds or notifies a downstream LDM. This function returns either NULL or a * reply to be sent to the downstream LDM (e.g., a RECLASS message) or * terminates this process (hopefully after sending some data). * * @param xprt [in/out] Pointer to server-side transport handle. * @param want [in] Pointer to subscription by downstream LDM. * May contain a "signature" product-specification. * @param isNotifier [in] Whether or not the upstream LDM is a feeder or a * notifier. * @param maxHereis Maximum HEREIS size parameter. Ignored if "isNotifier" * is true. * @return The reply for the downstream LDM or NULL if no reply * should be made. */ static fornme_reply_t* feed_or_notify( SVCXPRT* const xprt, const prod_class_t* const want, const int isNotifier, const max_hereis_t maxHereis) { struct sockaddr_in downAddr = *svc_getcaller(xprt); ErrorObj* errObj; int status; char* downName = NULL; prod_class_t* origSub = NULL; prod_class_t* allowSub = NULL; const signaturet* signature = NULL; UpFilter* upFilter = NULL; fornme_reply_t* reply = NULL; int isPrimary; static fornme_reply_t theReply; static prod_class_t* uldbSub = NULL; /* * Clean-up from a (possibly) previous invocation */ (void)memset(&theReply, 0, sizeof(theReply)); if (uldbSub != NULL) { free_prod_class(uldbSub); uldbSub = NULL; } downName = strdup(hostbyaddr(&downAddr)); if (NULL == downName) { LOG_ADD1("Couldn't duplicate downstream host name: \"%s\"", hostbyaddr(&downAddr)); log_log(LOG_ERR); svcerr_systemerr(xprt); goto return_or_exit; } set_abbr_ident(downName, isNotifier ? "(noti)" : "(feed)"); /* * Remove any "signature" specification from the subscription. */ if ((errObj = separateProductClass(want, &origSub, &signature)) != NULL) { err_log_and_free(errObj, ERR_FAILURE); svcerr_systemerr(xprt); goto free_down_name; } /* * Get the upstream filter */ errObj = lcf_getUpstreamFilter(downName, &downAddr.sin_addr, origSub, &upFilter); if (errObj) { err_log_and_free(ERR_NEW(0, errObj, "Couldn't get \"upstream\" filter"), ERR_FAILURE); svcerr_systemerr(xprt); goto free_orig_sub; } if (NULL == upFilter) { err_log_and_free(ERR_NEW1(0, NULL, "Upstream filter prevents data-transfer: %s", s_prod_class(NULL, 0, origSub)), ERR_FAILURE); svcerr_weakauth(xprt); goto free_orig_sub; } /* TODO: adjust time? */ /* * Reduce the subscription according to what the downstream host is allowed * to receive. */ status = lcf_reduceToAllowed(downName, &downAddr.sin_addr, origSub, &allowSub); if (status == ENOMEM) { LOG_SERROR0("Couldn't compute wanted/allowed product intersection"); log_log(LOG_ERR); svcerr_systemerr(xprt); goto free_up_filter; } if (status == EINVAL) { LOG_ADD1("Invalid pattern in product-class: %s", s_prod_class(NULL, 0, origSub)); log_log(LOG_WARNING); theReply.code = BADPATTERN; reply = &theReply; goto free_up_filter; } assert(status == 0); (void) logIfReduced(origSub, allowSub, "ALLOW entries"); /* * Reduce the subscription according to existing subscriptions from the * same downstream host and, if `isAntiDosEnabled()` returns `true`, * terminate every previously-existing upstream LDM process that's feeding * (not notifying) a subset of the subscription to the same IP address. * * The following relies on atexit()-registered cleanup for removal of the * entry from the upstream LDM database. */ isPrimary = maxHereis > UINT_MAX / 2; status = uldb_addProcess(getpid(), 6, &downAddr, allowSub, &uldbSub, isNotifier, isPrimary); if (status) { LOG_ADD0("Couldn't add this process to the upstream LDM database"); log_log(LOG_ERR); svcerr_systemerr(xprt); goto free_allow_sub; } (void) logIfReduced(allowSub, uldbSub, "existing subscriptions"); /* * Send a RECLASS reply to the downstream LDM if appropriate. */ if (!clss_eq(origSub, uldbSub)) { theReply.code = RECLASS; if (0 < uldbSub->psa.psa_len) { /* * The downstream LDM is allowed less than it requested and was * entered into the upstream LDM database. */ (void)uldb_remove(getpid()); /* maybe next time */ theReply.fornme_reply_t_u.prod_class = uldbSub; } else { /* * The downstream LDM isn't allowed anything and wasn't entered * into the upstream LDM database. */ static prod_class noSub = { { 0, 0 }, /* TS_ZERO */ { 0, 0 }, /* TS_ZERO */ { 0, (prod_spec *) NULL } }; theReply.fornme_reply_t_u.prod_class = &noSub; } reply = &theReply; goto free_allow_sub; } /* * Reply to the downstream LDM that the subscription will be honored. */ theReply.code = OK; theReply.fornme_reply_t_u.id = (unsigned) getpid(); if (!svc_sendreply(xprt, (xdrproc_t)xdr_fornme_reply_t, (caddr_t)&theReply)) { LOG_ADD0("svc_sendreply(...) failure"); log_log(LOG_ERR); svcerr_systemerr(xprt); goto free_allow_sub; } /* * Wait a second before sending anything to the downstream LDM. */ (void) sleep(1); status = isNotifier ? up6_new_notifier(xprt->xp_sock, downName, &downAddr, uldbSub, signature, getQueuePath(), interval, upFilter) : up6_new_feeder(xprt->xp_sock, downName, &downAddr, uldbSub, signature, getQueuePath(), interval, upFilter, isPrimary); svc_destroy(xprt); /* closes the socket */ exit(status); /* * Reply and error handling: */ free_allow_sub: free_prod_class(allowSub); free_up_filter: upFilter_free(upFilter); free_orig_sub: free_prod_class(origSub); free_down_name: free(downName); return_or_exit: return reply; }
int main( int ac, char *av[] ) { const char* const pqfname = getQueuePath(); const char* const progname = ubasename(av[0]); int useProductID = FALSE; int signatureFromId = FALSE; char *productID = NULL; int multipleFiles = FALSE; char identifier[KEYSIZE]; int status; int seq_start = 0; enum ExitCode { exit_success = 0, /* all files inserted successfully */ exit_system = 1, /* operating-system failure */ exit_pq_open = 2, /* couldn't open product-queue */ exit_infile = 3, /* couldn't process input file */ exit_dup = 4, /* input-file already in product-queue */ exit_md5 = 6 /* couldn't initialize MD5 processing */ } exitCode = exit_success; #if !USE_MMAP pqeIndex = PQE_NONE; #endif { extern int optind; extern int opterr; extern char *optarg; int ch; (void) openulog(progname, LOG_NOTIME, LOG_LDM, "-"); (void) setulogmask(LOG_UPTO(LOG_NOTICE)); opterr = 0; /* Suppress getopt(3) error messages */ while ((ch = getopt(ac, av, ":ivxl:q:f:s:p:")) != EOF) switch (ch) { case 'i': signatureFromId = 1; break; case 'v': (void) setulogmask(getulogmask() | LOG_MASK(LOG_INFO)); break; case 'x': (void) setulogmask(getulogmask() | LOG_MASK(LOG_DEBUG)); break; case 'l': openulog(progname, ulog_get_options(), LOG_LDM, optarg); break; case 'q': setQueuePath(optarg); break; case 's': seq_start = atoi(optarg); break; case 'f': feedtype = atofeedtypet(optarg); if(feedtype == NONE) { fprintf(stderr, "Unknown feedtype \"%s\"\n", optarg); usage(progname); } break; case 'p': useProductID = TRUE; productID = optarg; break; case ':': { LOG_ADD1("Option \"-%c\" requires an operand", optopt); usage(progname); } /* no break */ default: LOG_ADD1("Unknown option: \"%c\"", optopt); usage(progname); /* no break */ } ac -= optind; av += optind ; if(ac < 1) usage(progname); } /* * register exit handler */ if(atexit(cleanup) != 0) { serror("atexit"); exit(exit_system); } /* * set up signal handlers */ set_sigactions(); /* * who am i, anyway */ (void) strncpy(myname, ghostname(), sizeof(myname)); myname[sizeof(myname)-1] = 0; /* * open the product queue */ if(status = pq_open(pqfname, PQ_DEFAULT, &pq)) { if (PQ_CORRUPT == status) { uerror("The product-queue \"%s\" is inconsistent\n", pqfname); } else { uerror("pq_open: \"%s\" failed: %s", pqfname, status > 0 ? strerror(status) : "Internal error"); } exit(exit_pq_open); } { char *filename; int fd; struct stat statb; product prod; MD5_CTX *md5ctxp = NULL; /* * Allocate an MD5 context */ md5ctxp = new_MD5_CTX(); if(md5ctxp == NULL) { serror("new_md5_CTX failed"); exit(exit_md5); } /* These members are constant over the loop. */ prod.info.origin = myname; prod.info.feedtype = feedtype; if (ac > 1) { multipleFiles = TRUE; } for(prod.info.seqno = seq_start ; ac > 0 ; av++, ac--, prod.info.seqno++) { filename = *av; fd = open(filename, O_RDONLY, 0); if(fd == -1) { serror("open: %s", filename); exitCode = exit_infile; continue; } if( fstat(fd, &statb) == -1) { serror("fstat: %s", filename); (void) close(fd); exitCode = exit_infile; continue; } /* Determine what to use for product identifier */ if (useProductID) { if (multipleFiles) { sprintf(identifier,"%s.%d", productID, prod.info.seqno); prod.info.ident = identifier; } else prod.info.ident = productID; } else prod.info.ident = filename; prod.info.sz = statb.st_size; prod.data = NULL; /* These members, and seqno, vary over the loop. */ status = set_timestamp(&prod.info.arrival); if(status != ENOERR) { serror("set_timestamp: %s, filename"); exitCode = exit_infile; continue; } #if USE_MMAP prod.data = mmap(0, prod.info.sz, PROT_READ, MAP_PRIVATE, fd, 0); if(prod.data == NULL) { serror("mmap: %s", filename); (void) close(fd); exitCode = exit_infile; continue; } status = signatureFromId ? mm_md5(md5ctxp, prod.info.ident, strlen(prod.info.ident), prod.info.signature) : mm_md5(md5ctxp, prod.data, prod.info.sz, prod.info.signature); (void)exitIfDone(1); if (status != 0) { serror("mm_md5: %s", filename); (void) munmap(prod.data, prod.info.sz); (void) close(fd); exitCode = exit_infile; continue; } /* These members, and seqno, vary over the loop. */ status = set_timestamp(&prod.info.arrival); if(status != ENOERR) { serror("set_timestamp: %s, filename"); exitCode = exit_infile; continue; } /* * Do the deed */ status = pq_insert(pq, &prod); switch (status) { case ENOERR: /* no error */ if(ulogIsVerbose()) uinfo("%s", s_prod_info(NULL, 0, &prod.info, ulogIsDebug())) ; break; case PQUEUE_DUP: uerror("Product already in queue: %s", s_prod_info(NULL, 0, &prod.info, 1)); exitCode = exit_dup; break; case PQUEUE_BIG: uerror("Product too big for queue: %s", s_prod_info(NULL, 0, &prod.info, 1)); exitCode = exit_infile; break; case ENOMEM: uerror("queue full?"); exitCode = exit_system; break; case EINTR: #if defined(EDEADLOCK) && EDEADLOCK != EDEADLK case EDEADLOCK: /*FALLTHROUGH*/ #endif case EDEADLK: /* TODO: retry ? */ /*FALLTHROUGH*/ default: uerror("pq_insert: %s", status > 0 ? strerror(status) : "Internal error"); break; } (void) munmap(prod.data, prod.info.sz); #else // USE_MMAP above; !USE_MMAP below status = signatureFromId ? mm_md5(md5ctxp, prod.info.ident, strlen(prod.info.ident), prod.info.signature) : fd_md5(md5ctxp, fd, statb.st_size, prod.info.signature); (void)exitIfDone(1); if (status != 0) { serror("xx_md5: %s", filename); (void) close(fd); exitCode = exit_infile; continue; } if(lseek(fd, 0, SEEK_SET) == (off_t)-1) { serror("rewind: %s", filename); (void) close(fd); exitCode = exit_infile; continue; } pqeIndex = PQE_NONE; status = pqe_new(pq, &prod.info, &prod.data, &pqeIndex); if(status != ENOERR) { serror("pqe_new: %s", filename); exitCode = exit_infile; } else { ssize_t nread = read(fd, prod.data, prod.info.sz); (void)exitIfDone(1); if (nread != prod.info.sz) { serror("read %s %u", filename, prod.info.sz); status = EIO; } else { status = pqe_insert(pq, pqeIndex); pqeIndex = PQE_NONE; switch (status) { case ENOERR: /* no error */ if(ulogIsVerbose()) uinfo("%s", s_prod_info(NULL, 0, &prod.info, ulogIsDebug())) ; break; case PQUEUE_DUP: uerror("Product already in queue: %s", s_prod_info(NULL, 0, &prod.info, 1)); exitCode = exit_dup; break; case ENOMEM: uerror("queue full?"); break; case EINTR: #if defined(EDEADLOCK) && EDEADLOCK != EDEADLK case EDEADLOCK: /*FALLTHROUGH*/ #endif case EDEADLK: /* TODO: retry ? */ /*FALLTHROUGH*/ default: uerror("pq_insert: %s", status > 0 ? strerror(status) : "Internal error"); } } /* data read into `pqeIndex` region */ if (status != ENOERR) { (void)pqe_discard(pq, pqeIndex); pqeIndex = PQE_NONE; } } /* `pqeIndex` region allocated */ #endif (void) close(fd); } /* input-file loop */ free_MD5_CTX(md5ctxp); } /* code block */ exit(exitCode); }
int main(int ac, char *av[]) { int logfd; int width; int ready; unsigned long idle; fd_set readfds; fd_set exceptfds; struct timeval timeo; const char* const progname = ubasename(av[0]); unsigned logopts = LOG_CONS|LOG_PID; int logmask = LOG_UPTO(LOG_NOTICE); unsigned long maxProductSize = DEFAULT_MAX_PRODUCT_SIZE; /* * Setup default logging before anything else. */ logfd = openulog(progname, logopts, LOG_LDM, logpath); (void) setulogmask(logmask); feedtype = whatami(av[0]); { extern int optind; extern int opterr; extern char *optarg; int ch; opterr = 0; /* stops getopt() from printing to stderr */ usePil = 1; useNex = 1; pqpath = getQueuePath(); while ((ch = getopt(ac, av, ":vxcni5Nl:b:p:P:T:q:r:f:s:")) != EOF) switch (ch) { case 'v': logmask |= LOG_MASK(LOG_INFO); (void) setulogmask(logmask); break; case 'x': logmask |= LOG_MASK(LOG_DEBUG); (void) setulogmask(logmask); break; case 'c': chkflag = CHK_CHECK; break; case 'n': chkflag = CHK_DONT; break; case 'i': usePil = 0; break; case 'N': useNex = 0; break; case '5': skipLeadingCtlString = 0; break; case 'l': { logpath = optarg; if (strcmp(logpath, "-") == 0) { logopts = LOG_NOTIME; } else { logopts = LOG_CONS | LOG_PID; (void) fclose(stderr); } logfd = openulog(progname, logopts, LOG_LDM, logpath); break; } case 'b': baud = optarg; break; case 'p': parity = optarg; break; #if NET case 'P': *((int *)&server_port) = atoi(optarg); /* cast away const */ if(server_port <= 0 || server_port > 65536) { LOG_ADD1("Invalid server port: \"%s\"", optarg); log_log(LOG_ERR); usage(progname); } break; case 'T': reset_secs = atoi(optarg); if(reset_secs < 0) { LOG_ADD1("Invalid timeout: \"%s\"", optarg); usage(progname); } break; #endif /* NET */ case 's': { unsigned long size; int nbytes; if (sscanf(optarg, "%lu %n", &size, &nbytes) != 1 || optarg[nbytes] != 0 || 1 > size) { LOG_ADD1("Invalid maximum data-product size: \"%s\"", optarg); log_log(LOG_ERR); usage(progname); } maxProductSize = size; break; } case 'q': pqpath = optarg; break; case 'r': rawfname = optarg; break; case 'f': { feedtypet type; type = atofeedtypet(optarg); if(type != NONE) { feedtype = type; if(!parity && !baud) setFeedDefaults(type); } } break; case '?': { LOG_ADD1("Unknown option: \"%c\"", optopt); usage(progname); break; } case ':': /*FALLTHROUGH*/ default: LOG_ADD1("Missing argument for option: \"%c\"", optopt); usage(progname); break; } /* last arg, feedfname, is required */ if(ac - optind != 1) { LOG_ADD1("Wrong number of operands: %d", ac - optind); usage(progname); } (void)strncat(feedfname, av[optind], sizeof(feedfname)-6); } unotice("Starting Up"); udebug(PACKAGE_VERSION); if(logpath == NULL || !(*logpath == '-' && logpath[1] == 0)) { if (logfd < 0) { uerror("logfd < 0"); return 1; } setbuf(fdopen(logfd, "a"), NULL); } /* * register exit handler */ if(atexit(cleanup) != 0) { serror("atexit"); return 1; } /* * set up signal handlers */ set_sigactions(); /* * open the product queue, unless we were invoked as "feedtest" */ if(strcmp(progname, "feedtest") != 0) { if((ready = pq_open(pqpath, PQ_DEFAULT, &pq))) { if (PQ_CORRUPT == ready) { uerror("The product-queue \"%s\" is inconsistent\n", pqpath); } else { uerror("pq_open: \"%s\" failed: %s", pqpath, strerror(ready)); } return 1; } } /* * who am i, anyway */ (void) strncpy(myname, ghostname(), sizeof(myname)); myname[sizeof(myname)-1] = 0; /* * open the feed */ if(!(*feedfname == '-' && feedfname[1] == 0) && logfd != 0) (void) close(0); if(open_feed(feedfname, &ifd, maxProductSize) != ENOERR) return 1; if(usePil == 1) { if ((feedtype & DDS)||(feedtype & PPS)||(feedtype & IDS)|| (feedtype & HRS)) { usePil = 1; uinfo("Creating AFOS-like pil tags\0"); } else { usePil = 0; } } if (feedtype & HDS) { if(chkflag == CHK_CHECK || (isatty(ifd) && chkflag != CHK_DONT)) setTheScanner(scan_wmo_binary_crc); else setTheScanner(scan_wmo_binary); } else if (feedtype == ( DDPLUS | IDS ) ) { /* this is the combined NOAAPORT fos-alike. We know these have the 4 byte start and end sequences. Using the binary scanner ensures that we don't stop on an arbitray embedded CTRL-C */ unotice("Note: Using the wmo_binary scanner for SDI ingest\0"); setTheScanner (scan_wmo_binary); } else if (feedtype & (NMC2 | NMC3)) { setTheScanner(scan_wmo_binary); } else if (feedtype == AFOS) { prod_stats = afos_stats; setTheScanner(scan_afos); } else if (feedtype == FAA604) { prod_stats = faa604_stats; if(chkflag == CHK_CHECK || (isatty(ifd) && chkflag != CHK_DONT && parity != NULL && *parity != 'n') ) { setTheScanner(scan_faa604_parity); } else { setTheScanner(scan_faa604); } } else { if(chkflag == CHK_CHECK || (isatty(ifd) && chkflag != CHK_DONT && parity != NULL && *parity != 'n') ) { setTheScanner(scan_wmo_parity); } else { setTheScanner(scan_wmo); } } /* * Allocate an MD5 context */ md5ctxp = new_MD5_CTX(); if(md5ctxp == NULL) { serror("new_md5_CTX failed"); return 1; } /* * Main Loop */ idle = 0; while(exitIfDone(0)) { #if NET if (INPUT_IS_SOCKET) { if (port_error) { /* * lost connection => close */ if (ifd >= 0) { if(feed_close) (*feed_close)(ifd); ifd = -1; } port_error = 0; sleep (2); /* allow things to settle down */ continue; } } #endif if(stats_req) { unotice("Statistics Request"); if(pq != NULL) { off_t highwater = 0; size_t maxregions = 0; (void) pq_highwater(pq, &highwater, &maxregions); unotice(" Queue usage (bytes):%8ld", (long)highwater); unotice(" (nregions):%8ld", (long)maxregions); } unotice(" Idle: %8lu seconds", idle); #if NET if (INPUT_IS_SOCKET) { unotice(" Timeout: %8d", reset_secs); } #endif unotice("%21s: %s", "Status", (ifd < 0) ? "Not connected or input not open." : "Connected."); (*prod_stats)(); (*feed_stats)(); stats_req = 0; } #if NET if (INPUT_IS_SOCKET) { if (ifd < 0) { /* Attempt reconnect */ static int retries = 0; if (retries > MAX_RETRIES) { uerror ("maximum retry attempts %d, aborting", MAX_RETRIES); done = !0; continue; } /* Try to reopen on tcp read errors */ unotice("Trying to re-open connection on port %d", server_port); ++retries; if(open_feed(feedfname, &ifd, maxProductSize) != ENOERR) { unotice ("sleeping %d seconds before retry %d", retries * RETRY_DELAY, retries+1); sleep (retries * RETRY_DELAY); continue; } retries = 0; } } #endif /* NET */ timeo.tv_sec = 3; timeo.tv_usec = 0; FD_ZERO(&readfds); FD_ZERO(&exceptfds); FD_SET(ifd, &readfds); FD_SET(ifd, &exceptfds); width = ifd + 1; ready = select(width, &readfds, 0, &exceptfds, &timeo); if(ready < 0 ) { /* handle EINTR as a special case */ if(errno == EINTR) { errno = 0; continue; } serror("select"); return 1; } /* else */ #if 0 if (FD_ISSET(ifd, &exceptfds)) { uerror("Exception on input fd %d, select returned %d", ifd, ready); } #endif if(ready > 0) { /* do some work */ if(FD_ISSET(ifd, &readfds) || FD_ISSET(ifd, &exceptfds)) { idle = 0; if(feedTheXbuf(ifd) != ENOERR) { #if NET if (INPUT_IS_SOCKET) { port_error = !0; continue; } /* else */ #endif /* NET */ done = !0; } FD_CLR(ifd, &readfds); FD_CLR(ifd, &exceptfds); } else { uerror("select returned %d but ifd not set", ready); idle += timeo.tv_sec; } } else /* ready == 0 */ { idle += timeo.tv_sec; #if NET if (INPUT_IS_SOCKET) { /* VOODOO * This is necessary to stimulate * 'Connection reset by peer' * when the Portmaster goes down and comes * back up. */ static char zed[1] = {0}; if(write(ifd, zed, sizeof(zed)) < 0) { port_error = !0; continue; } } #endif } #if NET if (INPUT_IS_SOCKET) { if ((reset_secs > 0) && (idle >= reset_secs)) { unotice("Idle for %ld seconds, reconnecting", idle); /* force reconnect */ port_error = !0; idle = 0; continue; } } #endif /* NET */ (void) scanTheXbuf(); } return 0; }