/** * Initializes this module: * - Opens the product-queue named by `getQueuePath()`; * - Redirects the standard input stream to the given input file; * - Initializes the structure `prod`; and * - Initializes the PRNG module associated with the function `random()`. * * @retval true if and only if successful. */ static bool pti_init(void) { int success = false; const char* const pqfname = getQueuePath(); int status = pq_open(pqfname, PQ_DEFAULT, &pq); if (PQ_CORRUPT == status) { log_add("The product-queue \"%s\" is corrupt\n", pqfname); } else if (status) { log_errno_q(status, "Couldn't open product-queue \"%s\"", pqfname); } else { prod.data = malloc(max_prod_size); if (prod.data == NULL) { log_syserr_q("Couldn't allocate buffer for data-product"); } else { (void)strncpy(myname, ghostname(), sizeof(myname)); myname[sizeof(myname)-1] = 0; prod.info.origin = myname; prod.info.feedtype = feedtype; srandom(1); // for `random()` seed[2] = random(); seed[1] = random(); seed[0] = random(); (void)seed48(seed); // for `mrand48()` success = true; } } return success; }
/* * Sends a single, open file to an LDM as a data-product. The number of bytes * to be sent is specified by the data-product's metadata. The bytes start at * the beginning of the file. * * Arguments: * proxy The LDM proxy data-structure. * fd The file-descriptor open on the file to be sent. * info The data-product's metadata. Must be completely set. * * Returns: * 0 Success. * SYSTEM_ERROR O/S failure. "log_add()" called. * CONNECTION_ABORTED The connection was aborted. "log_add()" * called. */ static int send_product( LdmProxy* proxy, int fd, prod_info* const info) { int status; product product; product.info = *info; product.data = mmap(NULL, info->sz, PROT_READ, MAP_PRIVATE, fd, 0); if (MAP_FAILED == product.data) { log_syserr_q("Couldn't memory-map file"); status = SYSTEM_ERROR; } else { status = lp_send(proxy, &product); if (LP_UNWANTED == status) { log_notice_q("Unwanted product: %s", s_prod_info(NULL, 0, info, log_is_enabled_debug)); status = 0; } (void)munmap(product.data, info->sz); } /* file is memory-mapped */ return status; }
/** * Returns the process-ID that's contained in a file. * * @param[in] pathname The pathname of the file. * @retval 0 The file doesn't exist. * @retval -1 Error. `log_add()` called. * @return The process-ID that was in the file. */ static pid_t getPidFromFile( const char* const pathname) { long pid; FILE* file = fopen(pathname, "r"); if (file == NULL) { if (errno == ENOENT) { pid = 0; } else { log_syserr_q("Couldn't open PID file \"%s\"", pathname); pid = -1; } } else { pid = parsePid(file); if (pid == -1) log_add("Couldn't get PID from file \"%s\"", pathname); (void)fclose(file); } // `file` is open return pid; }
ErrorObj* err_new( const int code, ErrorObj* const cause, const char* const file, const unsigned line, const char* const fmt, ...) { ErrorObj *err; log_assert(file != NULL); err = (ErrorObj*)malloc(sizeof(ErrorObj)); if (NULL == err) { log_syserr_q("malloc(%lu) failure", (unsigned long)sizeof(ErrorObj)); } else { err->line = line; err->file = file; if (NULL == fmt) { err->msg[0] = 0; err->msglen = 0; err->code = code; err->cause = cause; } else { va_list args; int nbytes; size_t size = sizeof(err->msg); va_start(args, fmt); nbytes = vsnprintf(err->msg, size, fmt, args); if (nbytes < 0) { nbytes = snprintf(err->msg, size, "err_new(): vsnprintf() failure: \"%s\"", fmt); } else if (nbytes >= size) { nbytes = size - 1; } va_end(args); err->msg[nbytes] = 0; err->msglen = (size_t)nbytes; log_assert(err->msglen < size); err->code = code; err->cause = cause; } } /* "err" allocated */ return err; }
/* * Allocates memory. * * Arguments: * nbytes The number of bytes to allocate * status Pointer to the status variable to be set. Shall not be * NULL. Upon successful return, "*status" will be 0; * otherwise it will be ENOMEM. * Returns: * NULL System error. "log_add()" called. "*status" is * ENOMEM. * else A pointer to the allocated memory. "*status" is 0. */ void* reg_malloc( const size_t nbytes, RegStatus* const status) { void* ptr = malloc(nbytes); if (NULL == ptr) { log_syserr_q("Couldn't allocate %lu bytes", nbytes); *status = ENOMEM; } else { *status = 0; } return ptr; }
/** * Returns the process-ID that's contained in a file. * * @param[in] file The file that contains the PID. * @retval -1 Error. `log_add()` called. * @return The process-ID that was in the file. */ static pid_t parsePid( FILE* const file) { long pid; if (fscanf(file, "%ld", &pid) != 1) { if (ferror(file)) { log_syserr_q("Couldn't parse PID"); } else { log_add("Couldn't parse PID"); } pid = -1; } return pid; }
static int fd_md5(MD5_CTX *md5ctxp, int fd, off_t st_size, signaturet signature) { ssize_t nread; char buf[8192]; MD5Init(md5ctxp); for (; exitIfDone(1) && st_size > 0; st_size -= (off_t)nread) { nread = read(fd, buf, sizeof(buf)); if(nread <= 0) { log_syserr_q("fd_md5: read"); return -1; } /* else */ MD5Update(md5ctxp, (unsigned char *)buf, (unsigned int)nread); } MD5Final((unsigned char*)signature, md5ctxp); return 0; }
static int fd_md5(MD5_CTX *md5ctxp, int fd, off_t st_size, signaturet signature) { int nread; unsigned char buf[8192]; MD5Init(md5ctxp); for(; st_size > 0; st_size -= nread ) { nread = read(fd, buf, sizeof(buf)); if(nread <= 0) { log_syserr_q("fd_md5: read"); return -1; } /* else */ MD5Update(md5ctxp, buf, nread); (void)exitIfDone(1); } MD5Final(signature, md5ctxp); return 0; }
/* * Clones the prefix of a string. Logs a message if an error occurs. * * ARGUMENTS: * clone Pointer to a pointer to the clone. Set upon successful * return. Shall not be NULL. The client should call * "free(*clone)" when the clone is no longer needed. * string Pointer to the string to clone. Shall not be NULL. * nbytes The number of bytes of prefix to clone * RETURNS: * 0 Success. "*clone" is not NULL. * ENOMEM System error. "log_add()" called. */ RegStatus reg_clonePrefix( char** const clone, const char* const string, size_t nbytes) { RegStatus status; char* copy = (char*)reg_malloc(nbytes+1, &status); if (NULL != copy) { strncpy(copy, string, nbytes)[nbytes] = 0; *clone = copy; status = 0; } else { log_syserr_q("Couldn't clone first %lu bytes of string \"%s\"", nbytes, string); status = ENOMEM; } return status; }
int main(int ac, char *av[]) { int status = 0; char* logfname = 0; /// Data directory, conffile paths may be relative const char* datadir; int interval = DEFAULT_INTERVAL; prod_spec spec; prod_class_t clss; int toffset = TOFFSET_NONE; int loggingToStdErr = 0; unsigned queue_size = 5000; const char* progname = basename(av[0]); unsigned logopts = LOG_CONS|LOG_PID; /* * Setup default logging before anything else. */ (void)log_init(progname); const char* pqfname = getQueuePath(); spec.feedtype = DEFAULT_FEEDTYPE; spec.pattern = DEFAULT_PATTERN; if(set_timestamp(&clss.from)) /* corrected by toffset below */ { int errnum = errno; log_error_q("Couldn't set timestamp: %s", strerror(errnum)); exit(EXIT_FAILURE); /*NOTREACHED*/ } clss.to = TS_ENDT; clss.psa.psa_len = 1; clss.psa.psa_val = &spec; /* * deal with the command line, set options */ { extern int optind; extern int opterr; extern char *optarg; int ch; int fterr; opterr = 1; while ((ch = getopt(ac, av, "vxel:d:f:q:o:p:i:t:")) != EOF) { switch (ch) { case 'v': if (!log_is_enabled_info) (void)log_set_level(LOG_LEVEL_INFO); break; case 'x': (void)log_set_level(LOG_LEVEL_DEBUG); break; case 'e': key = ftok("/etc/rc.d/rc.local",'R'); semkey = ftok("/etc/rc.d/rc.local",'e'); shmid = shmget(key, sizeof(edex_message) * queue_size, 0666 | IPC_CREAT); semid = semget(semkey, 2, 0666 | IPC_CREAT); break; case 'l': logfname = optarg; (void)log_set_destination(logfname); break; case 'd': setPqactDataDirPath(optarg); break; case 'f': fterr = strfeedtypet(optarg, &spec.feedtype); if(fterr != FEEDTYPE_OK) { log_error_q("Bad feedtype \"%s\", %s\n", optarg, strfeederr(fterr)); usage(progname); } break; case 'q': pqfname = optarg; break; case 'o': toffset = atoi(optarg); if(toffset == 0 && *optarg != '0') { log_error_q("invalid offset %s\n", optarg); usage(progname); } break; case 'i': interval = atoi(optarg); if(interval == 0 && *optarg != '0') { log_error_q("invalid interval %s\n", optarg); usage(progname); } break; case 't': pipe_timeo = atoi(optarg); if(pipe_timeo == 0 && *optarg != 0) { log_error_q("invalid pipe_timeo %s", optarg); usage(progname); } break; case 'p': spec.pattern = optarg; break; default: usage(progname); break; } } conffilename = getPqactConfigPath(); datadir = getPqactDataDirPath(); { int numOperands = ac - optind; if (1 < numOperands) { log_error_q("Too many operands"); usage(progname); } else if (1 == numOperands) { conffilename = av[optind]; } } } setQueuePath(pqfname); log_notice_q("Starting Up"); if ('/' != conffilename[0]) { /* * The pathname of the configuration-file is relative. Convert it * to absolute so that it can be (re)read even if the current * working directory changes. */ #ifdef PATH_MAX char buf[PATH_MAX]; /* includes NUL */ #else char buf[_POSIX_PATH_MAX]; /* includes NUL */ #endif if (getcwd(buf, sizeof(buf)) == NULL) { log_syserr_q("Couldn't get current working directory"); exit(EXIT_FAILURE); } (void)strncat(buf, "/", sizeof(buf)-strlen(buf)-1); (void)strncat(buf, conffilename, sizeof(buf)-strlen(buf)-1); conffilename = strdup(buf); if (conffilename == NULL) { log_syserr_q("Couldn't duplicate string \"%s\"", buf); exit(EXIT_FAILURE); } } /* * Initialize the previous-state module for this process. */ if (stateInit(conffilename) < 0) { log_error_q("Couldn't initialize previous-state module"); exit(EXIT_FAILURE); /*NOTREACHED*/ } /* * Configure the standard I/O streams for execution of child processes. */ if (configure_stdio_file_descriptors()) { log_error_q("Couldn't configure standard I/O streams for execution " "of child processes"); exit(EXIT_FAILURE); } /* * Inform the "filel" module about the number of available file * descriptors. File descriptors are reserved for stdin, stdout, * stderr, the product-queue, the configuration-file, and (possibly) * logging. */ if (0 != set_avail_fd_count(openMax() - 6)) { log_error_q("Couldn't set number of available file-descriptors"); log_notice_q("Exiting"); exit(EXIT_FAILURE); /*NOTREACHED*/ } /* * Inform the "filel" module of the shared memory segment */ if (shmid != -1 && semid != -1) { set_shared_space(shmid, semid, queue_size); } /* * Compile the pattern. */ if (re_isPathological(spec.pattern)) { log_error_q("Adjusting pathological regular-expression: \"%s\"", spec.pattern); re_vetSpec(spec.pattern); } status = regcomp(&spec.rgx, spec.pattern, REG_EXTENDED|REG_NOSUB); if(status != 0) { log_error_q("Can't compile regular expression \"%s\"", spec.pattern); log_notice_q("Exiting"); exit(EXIT_FAILURE); /*NOTREACHED*/ } /* * register exit handler */ if(atexit(cleanup) != 0) { log_syserr_q("atexit"); log_notice_q("Exiting"); exit(EXIT_FAILURE); /*NOTREACHED*/ } /* * set up signal handlers */ set_sigactions(); /* * Read in (compile) the configuration file. We do this first so * its syntax may be checked without opening a product queue. */ if ((status = readPatFile(conffilename)) < 0) { exit(EXIT_FAILURE); /*NOTREACHED*/ } else if (status == 0) { log_notice_q("Configuration-file \"%s\" has no entries. " "You should probably not start this program instead.", conffilename); } /* * Open the product queue */ status = pq_open(pqfname, PQ_READONLY, &pq); if(status) { if (PQ_CORRUPT == status) { log_error_q("The product-queue \"%s\" is inconsistent\n", pqfname); } else { log_error_q("pq_open failed: %s: %s\n", pqfname, strerror(status)); } exit(EXIT_FAILURE); /*NOTREACHED*/ } if(toffset != TOFFSET_NONE) { /* * Filter and queue position set by "toffset". */ clss.from.tv_sec -= toffset; pq_cset(pq, &clss.from); } else { bool startAtTailEnd = true; timestampt insertTime; clss.from = TS_ZERO; /* * Try getting the insertion-time of the last, * successfully-processed data-product from the previous session. */ status = stateRead(&insertTime); if (status) { log_warning_q("Couldn't get insertion-time of last-processed " "data-product from previous session"); } else { timestampt now; (void)set_timestamp(&now); if (tvCmp(now, insertTime, <)) { log_warning_q("Time of last-processed data-product from previous " "session is in the future"); } else { char buf[80]; (void)strftime(buf, sizeof(buf), "%Y-%m-%d %T", gmtime(&insertTime.tv_sec)); log_notice_q("Starting from insertion-time %s.%06lu UTC", buf, (unsigned long)insertTime.tv_usec); pq_cset(pq, &insertTime); startAtTailEnd = false; } } if (startAtTailEnd) { log_notice_q("Starting at tail-end of product-queue"); (void)pq_last(pq, &clss, NULL); } }
/* * Returns: * 0 Success * SYSTEM_ERROR O/S failure. "log_add()" called. * LP_TIMEDOUT The RPC call timed-out. "log_add()" called. * LP_RPC_ERROR RPC error. "log_add()" called. * LP_LDM_ERROR LDM error. "log_add()" called. */ int main( int ac, char* av[]) { char myname[_POSIX_HOST_NAME_MAX]; char* progname = av[0]; prod_class_t clss; prod_spec spec; int seq_start = 0; int status; ErrorObj* error; unsigned remotePort = LDM_PORT; /* * Set up error logging */ (void)log_init(progname); remote = "localhost"; (void)set_timestamp(&clss.from); clss.to = TS_ENDT; clss.psa.psa_len = 1; clss.psa.psa_val = &spec; spec.feedtype = DEFAULT_FEEDTYPE; spec.pattern = ".*"; { extern int optind; extern char *optarg; int ch; while ((ch = getopt(ac, av, "vxl:h:f:P:s:")) != EOF) switch (ch) { case 'v': if (!log_is_enabled_info) (void)log_set_level(LOG_LEVEL_INFO); break; case 'x': (void)log_set_level(LOG_LEVEL_DEBUG); break; case 'l': (void)log_set_destination(optarg); break; case 'h': remote = optarg; break; case 'f': spec.feedtype = atofeedtypet(optarg); if(spec.feedtype == NONE) { fprintf(stderr, "Unknown feedtype \"%s\"\n", optarg); usage(progname); } 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]); } remotePort = (unsigned)port; break; } case 's': seq_start = atoi(optarg); break; case '?': usage(progname); break; } ac -= optind; av += optind; if(ac < 1) usage(progname); } /* * Register the exit handler */ if(atexit(cleanup) != 0) { log_syserr_q("atexit"); exit(SYSTEM_ERROR); } /* * Set up signal handlers */ set_sigactions(); (void) strncpy(myname, ghostname(), sizeof(myname)); myname[sizeof(myname)-1] = 0; (void)exitIfDone(INTERRUPTED); /* * Connect to the LDM. */ status = lp_new(remote, &ldmProxy); if (0 != status) { log_flush_error(); status = (LP_SYSTEM == status) ? SYSTEM_ERROR : CONNECTION_ABORTED; } else { log_debug("version %u", lp_version(ldmProxy)); status = ldmsend(ldmProxy, &clss, myname, seq_start, ac, av); if (0 != status) log_flush_error(); lp_free(ldmProxy); ldmProxy = NULL; } /* "ldmProxy" allocated */ return status; }
/* * Sends a list of files to the LDM as data-products. * * Arguments: * ldmProxy The LDM proxy data-structure. * offer The description of the class of data-products that this * process is willing to send. * origin The identifier of the host that created the * data-products (typically the host running this program). * seq_start The starting value of the data-product sequence number. * nfiles The number of files to send. * filenames The pathnames of the files to send. * * Returns: * 0 Success. * SYSTEM_ERROR O/S failure. "log_add()" called. * CONNECTION_ABORTED The connection was aborted. "log_add()" * called. */ static int ldmsend( LdmProxy* ldmProxy, prod_class_t* offer, char* origin, int seq_start, int nfiles, char* filenames[]) { int status = 0; char* filename; int fd; struct stat statb; prod_info info; MD5_CTX* md5ctxp = NULL; prod_class_t* want; /* * Allocate an MD5 context */ md5ctxp = new_MD5_CTX(); if (md5ctxp == NULL) { log_syserr_q("new_md5_CTX failed"); return SYSTEM_ERROR; } status = lp_hiya(ldmProxy, offer, &want); if (status != 0) { status = CONNECTION_ABORTED; } else { /* These members are constant over the loop. */ info.origin = origin; info.feedtype = offer->psa.psa_val->feedtype; for (info.seqno = seq_start; exitIfDone(1) && nfiles > 0; filenames++, nfiles--, info.seqno++) { filename = *filenames; info.ident = filename; /* * ?? This could be the creation time of the file. */ (void) set_timestamp(&info.arrival); /* * Checks 'arrival', 'feedtype', and 'ident' * against what the other guy has said he wants. */ if (!prodInClass(offer, &info)) { log_info_q("Not going to send %s", filename); continue; } if (!prodInClass(want, &info)) { log_info_q("%s doesn't want %s", lp_host(ldmProxy), filename); continue; } fd = open(filename, O_RDONLY, 0); if (fd == -1) { log_syserr_q("open: %s", filename); continue; } if (fstat(fd, &statb) == -1) { log_syserr_q("fstat: %s", filename); (void) close(fd); continue; } log_info_q("Sending %s, %d bytes", filename, statb.st_size); /* These members, and seqno, vary over the loop. */ if (fd_md5(md5ctxp, fd, statb.st_size, info.signature) != 0) { (void) close(fd); continue; } if (lseek(fd, 0, SEEK_SET) == (off_t)-1) { log_syserr_q("rewind: %s", filename); (void) close(fd); continue; } info.sz = (u_int)statb.st_size; (void)exitIfDone(1); status = send_product(ldmProxy, fd, &info); (void) close(fd); if (0 != status) { log_add("Couldn't send file \"%s\" to LDM", filename); break; } } /* file loop */ if (lp_flush(ldmProxy)) log_add("Couldn't flush connection"); free_prod_class(want); } /* HIYA succeeded */ free_MD5_CTX(md5ctxp); return status; }
/*ARGSUSED0*/ static void myStartElement( void* user_data, const xmlChar* name, const xmlChar** attrs) { switch (currState) { case START: if (xmlStrcmp(name, rootEltName) == 0) { currState = IN_ROOT_ELT; } else { log_error_q("Root element \"%s\" is not \"%s\"", name, rootEltName); currState = ERROR; } break; case IN_ROOT_ELT: if (xmlStrcmp(name, feedEltName) != 0) { prevState = currState; currState = UNKNOWN; unknownLevel = 1; } else { const xmlChar** nameValuePair = attrs; for (; *nameValuePair != NULL; nameValuePair += 2) { const xmlChar* attName = nameValuePair[0]; if (xmlStrcmp(attName, typeAttName) == 0) { if (strfeedtypet((const char*)nameValuePair[1], &feedType) != FEEDTYPE_OK) { log_error_q("Invalid feed type: \"%s\"", nameValuePair[1]); currState = ERROR; } } else if (xmlStrcmp(attName, idAttName) == 0) { prodId = strdup((char*)nameValuePair[1]); if (prodId == NULL) { log_syserr_q( "Couldn't duplicate product ID: \"%s\"", nameValuePair[1]); currState = ERROR; } } else if (xmlStrcmp(attName, sizeAttName) == 0) { if (sscanf((const char*)nameValuePair[1], "%u", &prodSize) != 1) { log_syserr_q("Invalid product size: \"%s\"", nameValuePair[1]); currState = ERROR; } } else if (xmlStrcmp(attName, delayAttName) == 0) { if (sscanf((const char*)nameValuePair[1], "%u", &delay) != 1) { log_syserr_q("Invalid delay time: \"%s\"", nameValuePair[1]); currState = ERROR; } } else { log_error_q("Invalid attribute: \"%s\"", attName); currState = ERROR; } } } break; case UNKNOWN: ++unknownLevel; break; } }
/** * 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_error_q("Couldn't duplicate downstream host name: \"%s\"", hostbyaddr(&downAddr)); svcerr_systemerr(xprt); goto return_or_exit; } log_set_upstream_id(downName, !isNotifier); /* * 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_syserr_q("Couldn't compute wanted/allowed product intersection"); svcerr_systemerr(xprt); goto free_up_filter; } if (status == EINVAL) { log_warning_q("Invalid pattern in product-class: %s", s_prod_class(NULL, 0, origSub)); 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_error_q("Couldn't add this process to the upstream LDM database"); 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_error_q("svc_sendreply(...) failure"); 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 progname = basename(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; (void)log_init(progname); #if !USE_MMAP pqeIndex = PQE_NONE; #endif { extern int optind; extern int opterr; extern char *optarg; int ch; 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': if (!log_is_enabled_info) (void)log_set_level(LOG_LEVEL_INFO); break; case 'x': (void)log_set_level(LOG_LEVEL_DEBUG); break; case 'l': (void)log_set_destination(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_add("Option \"-%c\" requires an operand", optopt); usage(progname); } /* no break */ default: log_add("Unknown option: \"%c\"", optopt); usage(progname); /* no break */ } ac -= optind; av += optind ; if(ac < 1) usage(progname); } const char* const pqfname = getQueuePath(); /* * register exit handler */ if(atexit(cleanup) != 0) { log_syserr_q("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) { log_error_q("The product-queue \"%s\" is inconsistent\n", pqfname); } else { log_error_q("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) { log_syserr_q("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) { log_syserr_q("open: %s", filename); exitCode = exit_infile; continue; } if( fstat(fd, &statb) == -1) { log_syserr_q("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) { log_syserr_q("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 == MAP_FAILED) { log_syserr_q("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) { log_syserr_q("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) { log_syserr_q("set_timestamp: %s, filename"); exitCode = exit_infile; continue; } /* * Do the deed */ status = pq_insert(pq, &prod); switch (status) { case ENOERR: /* no error */ if(log_is_enabled_info) log_info_q("%s", s_prod_info(NULL, 0, &prod.info, log_is_enabled_debug)) ; break; case PQUEUE_DUP: log_error_q("Product already in queue: %s", s_prod_info(NULL, 0, &prod.info, 1)); exitCode = exit_dup; break; case PQUEUE_BIG: log_error_q("Product too big for queue: %s", s_prod_info(NULL, 0, &prod.info, 1)); exitCode = exit_infile; break; case ENOMEM: log_error_q("queue full?"); exitCode = exit_system; break; case EINTR: #if defined(EDEADLOCK) && EDEADLOCK != EDEADLK case EDEADLOCK: /*FALLTHROUGH*/ #endif case EDEADLK: /* TODO: retry ? */ /*FALLTHROUGH*/ default: log_error_q("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) { log_syserr_q("xx_md5: %s", filename); (void) close(fd); exitCode = exit_infile; continue; } if(lseek(fd, 0, SEEK_SET) == (off_t)-1) { log_syserr_q("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) { log_syserr_q("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) { log_syserr_q("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()) log_info_q("%s", s_prod_info(NULL, 0, &prod.info, log_is_enabled_debug)) ; break; case PQUEUE_DUP: log_error_q("Product already in queue: %s", s_prod_info(NULL, 0, &prod.info, 1)); exitCode = exit_dup; break; case ENOMEM: log_error_q("queue full?"); break; case EINTR: #if defined(EDEADLOCK) && EDEADLOCK != EDEADLK case EDEADLOCK: /*FALLTHROUGH*/ #endif case EDEADLK: /* TODO: retry ? */ /*FALLTHROUGH*/ default: log_error_q("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); }
hiya_reply_t* hiya_6_svc( prod_class_t *offered, struct svc_req *rqstp) { const char* const pqfname = getQueuePath(); static hiya_reply_t reply; SVCXPRT * const xprt = rqstp->rq_xprt; struct sockaddr_in *upAddr = (struct sockaddr_in*) svc_getcaller(xprt); const char *upName = hostbyaddr(upAddr); int error; int isPrimary; unsigned int maxHereis; static prod_class_t *accept; /* * Open the product-queue for writing. It will be closed by cleanup() * during process termination. */ if (pq) { (void) pq_close(pq); pq = NULL; } error = pq_open(pqfname, PQ_DEFAULT, &pq); if (error) { err_log_and_free(ERR_NEW2(error, NULL, "Couldn't open product-queue \"%s\" for writing: %s", pqfname, PQ_CORRUPT == error ? "The product-queue is inconsistent" : strerror(error)), ERR_FAILURE); svcerr_systemerr(xprt); svc_destroy(xprt); exit(error); } /* else */ error = down6_init(upName, upAddr, pqfname, pq); if (error) { log_error_q("Couldn't initialize downstream LDM"); svcerr_systemerr(xprt); svc_destroy(xprt); exit(error); } else { log_info_q("Downstream LDM initialized"); } /* * The previous "accept" is freed here -- rather than freeing the * soon-to-be-allocated "accept" at the end of its block -- because it can * be used in the reply. */ if (accept) { free_prod_class(accept); /* NULL safe */ accept = NULL; } error = lcf_reduceToAcceptable(upName, inet_ntoa(upAddr->sin_addr), offered, &accept, &isPrimary); maxHereis = isPrimary ? UINT_MAX : 0; if (error) { log_syserr_q("Couldn't validate HIYA"); svcerr_systemerr(xprt); svc_destroy(xprt); exit(error); } else { if (log_is_enabled_debug) log_debug("intersection: %s", s_prod_class(NULL, 0, accept)); if (accept->psa.psa_len == 0) { log_warning_q("Empty intersection of HIYA offer from %s (%s) and ACCEPT " "entries", upName, s_prod_class(NULL, 0, offered)); svcerr_weakauth(xprt); svc_destroy(xprt); exit(0); } else { error = down6_set_prod_class(accept); if (error) { if (DOWN6_SYSTEM_ERROR == error) { log_syserr_q("Couldn't set product class: %s", s_prod_class(NULL, 0, accept)); } else { log_error_q("Couldn't set product class: %s", s_prod_class(NULL, 0, accept)); } svcerr_systemerr(xprt); svc_destroy(xprt); exit(EXIT_FAILURE); } /* else */ if (clss_eq(offered, accept)) { log_notice_q("hiya6: %s", s_prod_class(NULL, 0, offered)); reply.code = OK; reply.hiya_reply_t_u.max_hereis = maxHereis; } else { if (log_is_enabled_info) { char off[512]; char acc[512]; (void) s_prod_class(off, sizeof(off), offered), (void) s_prod_class( acc, sizeof(acc), accept); log_info_q("hiya6: RECLASS: %s -> %s", off, acc); } reply.code = RECLASS; reply.hiya_reply_t_u.feedPar.prod_class = accept; reply.hiya_reply_t_u.feedPar.max_hereis = maxHereis; } } /* product-intersection != empty set */ } /* successful acl_check_hiya() */ return &reply; }
/* * Opens the configuration file. Any previously opened configuration file will * be closed. The ulog(3) facility will be used to log any problems. * * pathname The pathname of the file. (in) * * Returns: * pq.h:ENOERR if success. * errno.h:ENOMEM if out of memory. * errno.h:EINVAL if the pathname is NULL or if the configuration file was * invalid. * (else) <errno.h> error code. */ int cfOpen(const char* pathname) { int status; if (pathname == NULL) { log_error_q("NULL pathname"); status = EINVAL; } else { if (currState != CLOSED) cfClose(); md5 = new_MD5_CTX(); if (saxer.startDocument != myStartDocument) { saxer.startDocument = myStartDocument; saxer.startElement = myStartElement; saxer.getEntity = myGetEntity; saxer.characters = myCharacters; saxer.endElement = myEndElement; saxer.endDocument = myEndDocument; saxer.warning = xmlWarning; saxer.error = xmlError; saxer.fatalError = xmlError; } (void)xmlSAXUserParseFile(&saxer, NULL, pathname); if (currState != READY) { cfClose(); status = EINVAL; } else { prodData = realloc(prodData, prodSize); if (prodData == NULL) { log_syserr_q("Couldn't allocate %u bytes for product data", prodSize); status = ENOMEM; } else { (void)memset(prodData, 'x', prodSize); prodInfo.origin = (char*)ghostname(); prodInfo.feedtype = feedType; prodInfo.ident = prodId; prodInfo.sz = prodSize; prodXdrLen = xlen_prod_i(&prodInfo); prodXdrBuf = realloc(prodXdrBuf, prodXdrLen); if (prodXdrBuf == NULL) { log_syserr_q("Couldn't allocate %lu bytes for product XDR " "buffer", prodXdrLen); status = errno; } else { prod.data = prodData; status = ENOERR; } if (status != ENOERR) { free(prodData); prodData = NULL; } } } } return status; }