static int setMcastInfo( McastInfo** const mcastInfo, const feedtypet feedtype) { ServiceAddr* mcastServAddr; int status = sa_new(&mcastServAddr, "224.0.0.1", 38800); if (status) { LOG_ADD0("Couldn't create multicast service address object"); } else { ServiceAddr* ucastServAddr; status = sa_new(&ucastServAddr, LOCAL_HOST, 0); if (status) { LOG_ADD0("Couldn't create unicast service address object"); } else { status = mi_new(mcastInfo, feedtype, mcastServAddr, ucastServAddr); if (status) { LOG_ADD0("Couldn't create multicast information object"); } else { sa_free(ucastServAddr); sa_free(mcastServAddr); } } } return status; }
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; }
/** * 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; }
/** * Vets a lock structure. * * @retval RWL_SUCCESS The lock structure is valid * @retval RWL_INVALID The lock structure is invalid. log_add() called. */ static srwl_Status vet( srwl_Lock* const lock /**< [in/out] the lock structure to vet */) { int status; if (lock->isValid != VALID_STRING) { LOG_ADD0("Invalid lock structure"); status = RWL_INVALID; } else { const pid_t pid = getpid(); if (pid != lock->pid) { /* * This process must be a child process. Reset the lock. NB: A * fork() zeroes the "semadj" value of semaphores in the child * process. */ lock->numReadLocks = 0; lock->numWriteLocks = 0; lock->pid = pid; } status = RWL_SUCCESS; } 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; }
/** * Only called once. */ static int setup(void) { /* * Ensure that the upstream component `up7` obtains the upstream queue from * `getQueuePath()`. This is not done for the downstream component because * `down7.c` implements an object-specific product-queue. */ setQueuePath(UP7_PQ_PATHNAME); setLdmLogDir("."); // For LDM-7 receiver session-memory files (*.yaml) int status = msm_init(); if (status) { LOG_ADD0("Couldn't initialize multicast sender map"); } else { msm_clear(); (void)sigemptyset(&termSigSet); (void)sigaddset(&termSigSet, SIGINT); (void)sigaddset(&termSigSet, SIGTERM); /* * The following allows a SIGTERM to be sent to the process group * without affecting the parent process (e.g., a make(1)). */ (void)setpgrp(); #if !USE_SIGWAIT status = initCondAndMutex(); #endif status = setTermSigHandler(); if (status) { LOG_ADD0("Couldn't set termination signal handler"); } } if (status) log_log(LOG_ERR); return status; }
/** * Called by `pthread_create()`. The thread is cancelled by * `sender_terminate()`. * * @param[in] arg Pointer to sender. * @retval &0 Success. Input end of sender's termination pipe(2) is closed. */ static void* sender_run( void* const arg) { Sender* const sender = (Sender*)arg; static int status; pthread_cleanup_push(freeLogging, NULL); const int servSock = sender->sock; struct pollfd fds; fds.fd = servSock; fds.events = POLLIN; for (;;) { status = poll(&fds, 1, -1); // `-1` => indefinite timeout int cancelState; (void)pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancelState); if (0 > status) break; if (fds.revents & POLLHUP) { status = 0; break; } if (fds.revents & POLLIN) { status = servlet_run(servSock); if (status) { LOG_ADD0("servlet_run() failure"); break; } } (void)pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &cancelState); } // `poll()` loop /* Because the current thread is ending: */ (status && !done) ? log_log(LOG_ERR) : log_clear(); // don't care about errors if termination requested pthread_cleanup_pop(1); // calls `log_free()` udebug("sender_run(): Returning &%d", status); return &status; }
/** * Sets the beginning-of-file response in a file-entry. * * @param[in,out] fileEntry The multicast file-entry in which to set the * BOF response. * @param[in] bofResponse Pointer to the beginning-of-file response. * @retval 0 Success. * @retval EINVAL if @code{fileEntry == NULL || bofResponse == * NULL}. \c log_add() called. */ int mcastFileEntry_setBofResponse( void* const fileEntry, const void* const bofResponse) { VcmtpFileEntry* const entry = (VcmtpFileEntry*)fileEntry; const BofResponse* const bof = (const BofResponse*)bofResponse; if (fileEntry == NULL || bofResponse == NULL) { LOG_ADD0("NULL argument"); return EINVAL; } entry->setBofResponse(bof); return 0; }
/** * 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; }
/** * 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; } }
/** * 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; }
int main( int ac, char* av[]) { const char* pqfname = getQueuePath(); int status; int doSomething = 1; in_addr_t ldmIpAddr = (in_addr_t) htonl(INADDR_ANY ); unsigned ldmPort = LDM_PORT; ensureDumpable(); /* * deal with the command line, set options */ { extern int optind; extern int opterr; extern char *optarg; int ch; int logmask = LOG_MASK(LOG_ERR) | LOG_MASK(LOG_WARNING) | LOG_MASK(LOG_NOTICE); opterr = 1; while ((ch = getopt(ac, av, "I:vxl:nq:o:P:M:m:t:")) != EOF) { switch (ch) { case 'I': { in_addr_t ipAddr = inet_addr(optarg); if ((in_addr_t) -1 == ipAddr) { (void) fprintf(stderr, "Interface specification \"%s\" " "isn't an IP address\n", optarg); exit(1); } ldmIpAddr = ipAddr; break; } case 'v': logmask |= LOG_MASK(LOG_INFO); break; case 'x': logmask |= LOG_MASK(LOG_DEBUG); break; case 'l': logfname = optarg; break; case 'q': pqfname = optarg; setQueuePath(optarg); break; case 'o': toffset = atoi(optarg); if (toffset == 0 && *optarg != '0') { (void) fprintf(stderr, "%s: invalid offset %s\n", av[0], optarg); usage(av[0]); } break; case 'P': { unsigned port; int nbytes; if (sscanf(optarg, "%5u %n", &port, &nbytes) != 1 || 0 != optarg[nbytes] || port > 0xffff) { (void)fprintf(stderr, "%s: invalid port number: %s\n", av[0], optarg); usage(av[0]); } ldmPort = port; break; } case 'M': { int max = atoi(optarg); if (max < 0) { (void) fprintf(stderr, "%s: invalid maximum number of clients %s\n", av[0], optarg); usage(av[0]); } maxClients = max; break; } case 'm': max_latency = atoi(optarg); if (max_latency <= 0) { (void) fprintf(stderr, "%s: invalid max_latency %s\n", av[0], optarg); usage(av[0]); } break; case 'n': doSomething = 0; break; case 't': rpctimeo = (unsigned) atoi(optarg); if (rpctimeo == 0 || rpctimeo > 32767) { (void) fprintf(stderr, "%s: invalid timeout %s", av[0], optarg); usage(av[0]); } break; case '?': usage(av[0]); break; } /* "switch" statement */ } /* argument loop */ if (ac - optind == 1) setLdmdConfigPath(av[optind]); (void) setulogmask(logmask); if (toffset != TOFFSET_NONE && toffset > max_latency) { (void) fprintf(stderr, "%s: invalid toffset (%d) > max_latency (%d)\n", av[0], toffset, max_latency); usage(av[0]); } } /* command-line argument decoding */ if (logfname != NULL && *logfname == '-') { /* * Logging to standard error stream. Assume interactive. * * Make this process a process group leader so that all child processes * (e.g., upstream LDM, downstream LDM, pqact(1)s) will be signaled by * `cleanup()`. */ (void)setpgid(0, 0); // can't fail } #ifndef DONTFORK else { /* * Logging to system logging daemon or file. Make this process a daemon. */ pid_t pid; pid = ldmfork(); if (pid == -1) { log_add("Couldn't fork LDM daemon"); log_log(LOG_ERR); exit(2); } if (pid > 0) { /* parent */ (void) printf("%ld\n", (long) pid); exit(0); } /* detach the child from parents process group ?? */ (void) setsid(); // also makes this process a process group leader } #endif /* * Initialize logger. * (Close fd 2 to remap stderr to the logfile, when * appropriate. I know, this is anal.) */ if (logfname == NULL ) (void) fclose(stderr); else if (!(logfname[0] == '-' && logfname[1] == 0)) (void) close(2); (void) openulog(ubasename(av[0]), (LOG_CONS | LOG_PID), LOG_LDM, logfname); unotice("Starting Up (version: %s; built: %s %s)", PACKAGE_VERSION, __DATE__, __TIME__); /* * register exit handler */ if (atexit(cleanup) != 0) { serror("atexit"); unotice("Exiting"); exit(1); } /* * set up signal handlers */ set_sigactions(); /* * Close the standard input and standard output streams because they won't * be used (more anality :-) */ (void) fclose(stdout); (void) fclose(stdin); /* * Vet the configuration file. */ udebug("main(): Vetting configuration-file"); if (read_conf(getLdmdConfigPath(), 0, ldmIpAddr, ldmPort) != 0) { log_log(LOG_ERR); exit(1); } if (doSomething) { int sock = -1; if (lcf_isServerNeeded()) { /* * Create a service portal. This should be done before anything is * created because this is the function that relinquishes superuser * privileges. */ udebug("main(): Creating service portal"); if (create_ldm_tcp_svc(&sock, ldmIpAddr, ldmPort) != ENOERR) { /* error reports are emitted from create_ldm_tcp_svc() */ exit(1); } udebug("tcp sock: %d", sock); } /* * Verify that the product-queue can be open for writing. */ udebug("main(): Opening product-queue"); if ((status = pq_open(pqfname, PQ_DEFAULT, &pq))) { if (PQ_CORRUPT == status) { uerror("The product-queue \"%s\" is inconsistent", pqfname); } else { uerror("pq_open failed: %s: %s", pqfname, strerror(status)); } exit(1); } (void) pq_close(pq); pq = NULL; /* * Create the sharable database of upstream LDM metadata. */ udebug("main(): Creating shared upstream LDM database"); if ((status = uldb_delete(NULL))) { if (ULDB_EXIST == status) { log_clear(); } else { LOG_ADD0( "Couldn't delete existing shared upstream LDM database"); log_log(LOG_ERR); exit(1); } } if (uldb_create(NULL, maxClients * 1024)) { LOG_ADD0("Couldn't create shared upstream LDM database"); log_log(LOG_ERR); exit(1); } /* * Initialize the multicast sender map. */ #if WANT_MULTICAST if (msm_init()) { LOG_ADD0("Couldn't initialize multicast LDM sender map"); log_log(LOG_ERR); exit(1); } #endif /* * Re-read (and execute) the configuration file (downstream LDM-s are * started). */ lcf_free(); // Start with a clean slate to prevent duplicates udebug("main(): Reading configuration-file"); if (read_conf(getLdmdConfigPath(), 1, ldmIpAddr, ldmPort) != 0) { log_log(LOG_ERR); exit(1); } if (lcf_isServerNeeded()) { /* * Serve */ udebug("main(): Serving socket"); sock_svc(sock); } else { /* * Wait until all child processes have terminated. */ while (reap(-1, 0) > 0) /* empty */; } } // configuration-file will be executed return (0); }
/** * 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* pqfname = getQueuePath(); int sock = -1; int status; int doSomething = 1; in_addr_t locIpAddr = (in_addr_t) htonl(INADDR_ANY ); unsigned ldmPort = LDM_PORT; ensureDumpable(); /* * deal with the command line, set options */ { extern int optind; extern int opterr; extern char *optarg; int ch; int logmask = LOG_MASK(LOG_ERR) | LOG_MASK(LOG_WARNING) | LOG_MASK(LOG_NOTICE); opterr = 1; while ((ch = getopt(ac, av, "I:vxl:nq:o:P:M:m:t:")) != EOF) { switch (ch) { case 'I': { in_addr_t ipAddr = inet_addr(optarg); if ((in_addr_t) -1 == ipAddr) { (void) fprintf(stderr, "Interface specification \"%s\" " "isn't an IP address\n", optarg); exit(1); } locIpAddr = ipAddr; break; } case 'v': logmask |= LOG_MASK(LOG_INFO); break; case 'x': logmask |= LOG_MASK(LOG_DEBUG); break; case 'l': logfname = optarg; break; case 'q': pqfname = optarg; setQueuePath(optarg); break; case 'o': toffset = atoi(optarg); if (toffset == 0 && *optarg != '0') { (void) fprintf(stderr, "%s: invalid offset %s\n", av[0], optarg); usage(av[0]); } break; case 'P': { char* suffix = ""; long port; errno = 0; port = strtol(optarg, &suffix, 0); if (0 != errno || 0 != *suffix || 0 >= port || 0xffff < port) { (void) fprintf(stderr, "%s: invalid port %s\n", av[0], optarg); usage(av[0]); } ldmPort = (unsigned) port; break; } case 'M': { int max = atoi(optarg); if (max < 0) { (void) fprintf(stderr, "%s: invalid maximum number of clients %s\n", av[0], optarg); usage(av[0]); } maxClients = max; break; } case 'm': max_latency = atoi(optarg); if (max_latency <= 0) { (void) fprintf(stderr, "%s: invalid max_latency %s\n", av[0], optarg); usage(av[0]); } break; case 'n': doSomething = 0; break; case 't': rpctimeo = (unsigned) atoi(optarg); if (rpctimeo == 0 || rpctimeo > 32767) { (void) fprintf(stderr, "%s: invalid timeout %s", av[0], optarg); usage(av[0]); } break; case '?': usage(av[0]); break; } /* "switch" statement */ } /* argument loop */ if (ac - optind == 1) setLdmdConfigPath(av[optind]); (void) setulogmask(logmask); if (toffset != TOFFSET_NONE && toffset > max_latency) { (void) fprintf(stderr, "%s: invalid toffset (%d) > max_latency (%d)\n", av[0], toffset, max_latency); usage(av[0]); } } /* command-line argument decoding */ #ifndef DONTFORK /* * daemon behavior * * Background the process unless we are logging to stderr, in which * case we assume interactive. */ if (logfname == NULL || *logfname != '-') { /* detach */ pid_t pid; pid = ldmfork(); if (pid == -1) { log_add("Couldn't fork LDM daemon"); log_log(LOG_ERR); exit(2); } if (pid > 0) { /* parent */ (void) printf("%ld\n", (long) pid); exit(0); } /* detach the child from parents process group ?? */ (void) setsid(); } #endif /* * Initialize logger. * (Close fd 2 to remap stderr to the logfile, when * appropriate. I know, this is anal.) */ if (logfname == NULL ) (void) fclose(stderr); else if (!(logfname[0] == '-' && logfname[1] == 0)) (void) close(2); (void) openulog(ubasename(av[0]), (LOG_CONS | LOG_PID), LOG_LDM, logfname); unotice("Starting Up (version: %s; built: %s %s)", PACKAGE_VERSION, __DATE__, __TIME__); /* * register exit handler */ if (atexit(cleanup) != 0) { serror("atexit"); unotice("Exiting"); exit(1); } /* * set up signal handlers */ set_sigactions(); /* * Close the standard input and standard output streams because they won't * be used (more anality :-) */ (void) fclose(stdout); (void) fclose(stdin); if (!doSomething) { /* * Vet the configuration file. */ udebug("main(): Vetting configuration-file"); if (read_conf(getLdmdConfigPath(), doSomething, ldmPort) != 0) { log_log(LOG_ERR); exit(1); } } else { /* * Create a service portal. This should be done before anything is * created because this is the function that relinquishes superuser * privileges. */ udebug("main(): Creating service portal"); if (create_ldm_tcp_svc(&sock, locIpAddr, ldmPort) != ENOERR) { /* error reports are emitted from create_ldm_tcp_svc() */ exit(1); } udebug("tcp sock: %d", sock); /* * Verify that the product-queue can be open for writing. */ udebug("main(): Opening product-queue"); if (status = pq_open(pqfname, PQ_DEFAULT, &pq)) { if (PQ_CORRUPT == status) { uerror("The product-queue \"%s\" is inconsistent", pqfname); } else { uerror("pq_open failed: %s: %s", pqfname, strerror(status)); } exit(1); } (void) pq_close(pq); pq = NULL; /* * Create the sharable database of upstream LDM metadata. */ udebug("main(): Creating shared upstream LDM database"); if (status = uldb_delete(NULL)) { if (ULDB_EXIST == status) { log_clear(); } else { LOG_ADD0( "Couldn't delete existing shared upstream LDM database"); log_log(LOG_ERR); exit(1); } } if (uldb_create(NULL, maxClients * 1024)) { LOG_ADD0("Couldn't create shared upstream LDM database"); log_log(LOG_ERR); exit(1); } /* * Read the configuration file (downstream LDM-s are started). */ udebug("main(): Reading configuration-file"); if (read_conf(getLdmdConfigPath(), doSomething, ldmPort) != 0) { log_log(LOG_ERR); exit(1); } /* * Serve */ udebug("main(): Serving socket"); sock_svc(sock); } /* "doSomething" is true */ return (0); }
/** * @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; }
/** * Aborts the process due to an error in logic. */ static void abortProcess(void) { LOG_ADD0("Logic error"); log_log(LOG_ERR); abort(); }
/*ARGSUSED*/ static int exec_prodput( const product* prod, int argc, char** argv, const void* xprod, size_t xlen) { pid_t pid = 0; if (NULL == execMap) { execMap = cm_new(); if (NULL == execMap) { LOG_ADD0("Couldn't create child-process map for EXEC entries"); log_log(LOG_ERR); pid = -1; } } /* child-process map not allocated */ if (0 == pid) { int waitOnChild = 0; /* default is not to wait */ if (strcmp(argv[0], "-wait") == 0) { waitOnChild = 1; /* => wait for child */ argc--; argv++; } pid = ldmfork(); if (-1 == pid) { LOG_SERROR0("Couldn't fork EXEC process"); log_log(LOG_ERR); } else { if (0 == pid) { /* * Child process. * * Detach the child process from the parents process group?? * * (void) setpgid(0,0); */ const unsigned ulogOptions = ulog_get_options(); const char* ulogIdent = getulogident(); const unsigned ulogFacility = getulogfacility(); const char* ulogPath = getulogpath(); (void)signal(SIGTERM, SIG_DFL); (void)pq_close(pq); /* * It is assumed that the standard input, output, and error * streams are correctly established and should not be * modified. */ /* * Don't let the child process get any inappropriate privileges. */ endpriv(); (void) execvp(argv[0], argv); openulog(ulogIdent, ulogOptions, ulogFacility, ulogPath); LOG_SERROR1("Couldn't execute command \"%s\"", argv[0]); log_log(LOG_ERR); exit(EXIT_FAILURE); } /* child process */ else { /* * Parent process. */ (void)cm_add_argv(execMap, pid, argv); if (!waitOnChild) { udebug(" exec %s[%d]", argv[0], pid); } else { udebug(" exec -wait %s[%d]", argv[0], pid); (void)reap(pid, 0); } } } /* child-process forked */ } /* child-process map allocated */ return -1 == pid ? -1 : 1; }