/* * Adds an entry to a child-map. * * @retval 0 Success * @retval 1 Usage error. \c log_start() called. * @retval 2 O/S failure. \c log_start() called. */ int cm_add_string( ChildMap* const map, /**< [in/out] Pointer to the child-map */ const pid_t pid, /**< [in] Process ID of the child. * Must not already exist in map. */ const char* const command) /**< [in] Command-line of the child. * Defensively copied. */ { int status; if (NULL == map) { LOG_START0("Null map argument"); status = 1; } else if (NULL == command) { LOG_START0("Null command argument"); status = 1; } else { status = cm_contains(map, pid); if (0 == status) { Entry* const entry = (Entry*)malloc(sizeof(Entry)); if (NULL == entry) { LOG_SERROR0("Couldn't allocate new entry"); status = 2; } else { entry->command = strdup(command); if (NULL == entry->command) { LOG_SERROR0("Couldn't duplicate command-line"); status = 2; } else { entry->pid = pid; if (NULL == tsearch(entry, &map->root, compare)) { LOG_SERROR0("Couldn't add entry to map"); status = 2; } else { map->count++; status = 0; } if (0 != status) free(entry->command); } /* "entry->command" allocated */ if (0 != status) free(entry); } /* "entry" allocated */ } /* "pid" not in map */ } /* valid arguments */ 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; }
static void waitUntilDone(void) { struct sigaction newAction; (void)sigemptyset(&newAction.sa_mask); newAction.sa_flags = 0; newAction.sa_handler = termSigHandler; struct sigaction oldAction; if (setTermSigHandling(&newAction, &oldAction)) { LOG_SERROR0("Couldn't set termination signal handling"); abortProcess(); } waitForDoneCondition(); if (setTermSigHandling(&oldAction, NULL)) { LOG_SERROR0("Couldn't reset termination signal handling"); abortProcess(); } }
/** * Returns a new instance of a child-map. * * @retval NULL Failure. \c log_start() called. * @retval !NULL Pointer to a new instance. */ ChildMap* cm_new(void) { ChildMap* map = (ChildMap*)malloc(sizeof(ChildMap)); if (NULL == map) { LOG_SERROR0("Couldn't allocate new child-map"); } else { map->root = NULL; map->count = 0; map->buf = strBuf_new(132); if (NULL == map->buf) { LOG_SERROR0("Couldn't allocate command-line buffer"); free(map); map = NULL; } } return map; }
static int mlsm_ensureCleanup(void) { if (cleanupRegistered) return 0; int status = atexit(mlsm_killChild); if (status) { LOG_SERROR0("Couldn't register cleanup routine"); status = LDM7_SYSTEM; } else { cleanupRegistered = true; } return status; }
/** * @param[in] newAction * @param[out] oldAction * @retval 0 Success * @retval ENOTSUP The SA_SIGINFO bit flag is set in the `sa_flags` field of * `newAction` and the implementation does not support either * the Realtime Signals Extension option, or the XSI Extension * option. */ static int setTermSigHandling( struct sigaction* newAction, struct sigaction* oldAction) { int status; if (sigaction(SIGINT, newAction, oldAction) || sigaction(SIGTERM, newAction, oldAction)) { LOG_SERROR0("sigaction() failure"); status = errno; } else { status = 0; } return status; }
/** * Obtains a read-write lock based on an existing semaphore set. * * @retval RWL_SUCCESS Success. * @retval RWL_EXIST The lock doesn't exist. * @retval RWL_SYSTEM System error. See "errno". log_add() called. */ static srwl_Status getLock( const key_t key /**< [in] the key associated with the semaphore */, int* const semId /**< [out] pointer to the semaphore identifier */) { srwl_Status status; int id = semget(key, SI_NUM_SEMS, read_write); if (-1 == id) { LOG_SERROR0("Couldn't get existing semaphore set"); status = RWL_EXIST; } else { *semId = id; status = RWL_SUCCESS; } return status; }
/** * Unconditionally deletes a read/write lock by IPC key. The Semaphore on * which the lock is based is deleted. * * @param key The IPC key * @retval RWL_SUCCESS Success * @retval RWL_EXIST The key has no associated read/write lock * @retval RWL_SYSTEM System error. log_add() called. */ srwl_Status srwl_deleteByKey( const key_t key) { int status = semget(key, 0, read_write); if (-1 == status) { LOG_SERROR0("Couldn't get semaphore set"); status = (ENOENT == errno) ? RWL_EXIST : RWL_SYSTEM; } else if (semctl(status, 0, IPC_RMID)) { LOG_SERROR1("Couldn't delete existing semaphore set %d", status); status = RWL_SYSTEM; } else { status = RWL_SUCCESS; } return status; }
/** * Creates a read/write lock based on creating a new semaphore set. Any previous * semaphore set will be deleted. * * @retval RWL_SUCCESS Success * @retval RWL_SYSTEM System error. See "errno". log_add() called. */ static srwl_Status createLock( const key_t key /**< [in] the key associated with the semaphore */, int* const semId /**< [out] pointer to the semaphore identifier */) { srwl_Status status; int id; (void) deleteSemSet(semget(key, 0, read_write)); log_clear(); id = semget(key, SI_NUM_SEMS, IPC_CREAT | IPC_EXCL | read_write); if (-1 == id) { LOG_SERROR0("Couldn't create semaphore set"); status = RWL_SYSTEM; } else { unsigned short semVal[SI_NUM_SEMS]; union semun { int val; /* Value for SETVAL */ struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */ unsigned short *array; /* Array for GETALL, SETALL */ } arg; semVal[SI_LOCK] = 1; semVal[SI_NUM_READERS] = 0; arg.array = semVal; if (semctl(id, 0, SETALL, arg) == -1) { LOG_SERROR1("Couldn't initialize semaphore set: semId=%d", id); (void) deleteSemSet(id); status = RWL_SYSTEM; } else { *semId = id; status = RWL_SUCCESS; } } return status; }
/** * Initializes logging. * * @retval 0 Success * @retval 1 Usage error. */ static int initLogging( const char* const progName, /**< [in] Name of the program */ const unsigned logOptions, /**< [in] Logging options */ const unsigned logFacility, /**< [in] Logging facility */ const char* const logPath) /**< [in] Pathname of the log file, * "-", or NULL */ { int status; if (openulog(progName, logOptions, logFacility, logPath) == -1) { LOG_SERROR0("Couldn't initialize logging"); log_log(LOG_ERR); usage(progName); status = 1; } else { status = 0; } return status; }
/** * Ensures the existence of a client-side transport on the TCP connection. * * @param[in] xprt Server-side RPC transport. * @retval true Success. * @retval false System error. `log_start()` called. */ static bool up7_ensureClientTransport( struct SVCXPRT* const xprt) { bool success; if (clnt) { success = true; } else { /* * Create a client-side RPC transport on the TCP connection. */ do { clnt = clnttcp_create(&xprt->xp_raddr, LDMPROG, SEVEN, &xprt->xp_sock, MAX_RPC_BUF_NEEDED, 0); /* TODO: adjust sending buffer size in above */ } while (clnt == NULL && rpc_createerr.cf_stat == RPC_TIMEDOUT); if (clnt == NULL ) { LOG_START2("Couldn't create client-side transport to downstream LDM-7 on " "%s%s", hostbyaddr(&xprt->xp_raddr), clnt_spcreateerror("")); success = false; } else { if (atexit(up7_cleanup)) { LOG_SERROR0("Couldn't register upstream LDM-7 cleanup function"); log_log(LOG_ERR); success = false; } else { success = true; } } } return success; }
/** * 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; }
/** * @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; }
/*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; }