/* * Check to make sure that the current process doesn't have the lock on a * shared-memory FIFO. * * Precondition: * The FIFO is not locked by this process. * Arguments: * shm Pointer to the shared-memory FIFO data-structure. * havLck Pointer to indicator to be set. * Returns: * 0 Success. The current process doesn't have the FIFO * locked. * EINVAL The current process has the FIFO locked. Error-message * logged. * EINVAL "shm" uninitialized. Error-message logged. * ECANCELED Operating-system failure. Error-message logged. */ static int checkUnlocked( const struct shmhandle* const shm) { int status; if (0 > shm->semid) { log_error("Invalid semaphore ID: %d", shm->semid); status = EINVAL; } else { int semval = semctl(shm->semid, SI_LOCK, GETVAL); int pid = semctl(shm->semid, SI_LOCK, GETPID); if (-1 == semval || -1 == pid) { log_syserr("semctl() failure"); status = ECANCELED; } else { if ((0 == semval) && (getpid() == pid)) { log_error("FIFO already locked by this process: %d", pid); status = EINVAL; } else { status = 0; } } } return status; }
/* * Check to make sure that the current process has the lock on a shared-memory * FIFO. * * Arguments: * shm Pointer to the shared-memory FIFO data-structure. * havLck Pointer to indicator to be set. * Returns: * 0 Success. The current process has the FIFO locked. * EINVAL The current process doesn't have the FIFO locked. * Error-message logged. * EINVAL "shm" uninitialized. Error-message logged. * ECANCELED Operating-system failure. Error-message logged. */ static int checkLocked( const struct shmhandle* const shm) { int status; if (0 > shm->semid) { log_error("Invalid semaphore ID: %d", shm->semid); status = EINVAL; } else { int semval = semctl(shm->semid, SI_LOCK, GETVAL); int pid = semctl(shm->semid, SI_LOCK, GETPID); if (-1 == semval || -1 == pid) { log_syserr("semctl() failure"); status = ECANCELED; } else { if (0 != semval) { log_error("FIFO not locked: %d", semval); status = EINVAL; } else if (getpid() != pid) { log_error("FIFO locked by another process: %d", pid); status = EINVAL; } else { status = 0; } } } return status; }
/* * Locks a shared-memory FIFO. * * Preconditions: * The shared-memory FIFO is unlocked. * Arguments: * shm Pointer to the shared-memory FIFO data-structure. * Returns: * 0 Success * ECANCELED Operating-system failure. Error-message logged. * EINVAL "shm" is uninitialized. Error-message logged. * EINVAL Semaphore is uninitialized. Error-message logged. * EINVAL FIFO is already locked by this process. Error-message * logged. */ static int shmfifo_lock( const struct shmhandle* const shm) { int status = checkUnlocked(shm); if (0 == status) { struct sembuf op[1]; /* printf("called shmfifo_lock semid: %d in process %d\n",shm->semid, * getpid()); * printf("<%d>locking %d\n",getpid(),shm->semid); */ op[0].sem_num = SI_LOCK; op[0].sem_op = -1; op[0].sem_flg = 0; /* dvbs_multicast(1) used to hang here */ if (semop (shm->semid, op, 1) == -1) { log_syserr("semop(2) failure"); status = ECANCELED; } else { status = 0; /* success */ } /* printf("<%d> locked\n",getpid()); */ } return status; }
/* * Unlocks a shared-memory FIFO. * * Precondition: * The FIFO is locked by this process. * Arguments: * shm Pointer to the shared-memory FIFO data-structure. * Returns: * 0 Success. * ECANCELED Operating-system failure. Error-message logged. * EINVAL "shm" is uninitialized. * EINVAL FIFO is locked by this process. Error-message logged. */ static int shmfifo_unlock( const struct shmhandle* const shm) { int status = checkLocked(shm); if (0 == status) { struct sembuf op[1]; /* printf("<%d> unlocking %d\n",getpid(),shm->semid); */ op[0].sem_num = SI_LOCK; op[0].sem_op = 1; /*op[0].sem_flg = SEM_UNDO; */ op[0].sem_flg = 0; if (semop(shm->semid, op, 1) == -1) { log_syserr("semop(2) failure"); status = ECANCELED; } else { status = 0; /* success */ } /* printf("unlocked. done\n"); */ } return status; }
/* * Waits for a shared-memory FIFO to be notified. The shared-memory FIFO must * be locked. * * Arguments: * shm Pointer to the shared-memory FIFO. * semIndex The index of the semaphore to wait upon. One of * SI_READER or SI_WRITER. * Returns: * 0 Success. Another thread of control has locked and * released the FIFO. Upon return, the shared-memory FIFO * shall be locked. * EINVAL "shm" uninitialized. Error-message logged. * EINVAL The FIFO isn't locked by the current process. * Error-message logged. * EINVAL "semIndex" isn't SI_READER or SI_WRITER. * ECANCELED Operating-system failure. Error-message logged. * Raises: * SIGSEGV if "shm" is NULL. */ static int shmfifo_wait( const struct shmhandle* const shm, const SemIndex semIndex) { int status = vetSemIndex(semIndex); if (0 == status) { /* Release the lock */ if ((status = shmfifo_unlock(shm)) == 0) { struct sembuf op[1]; /* Wait for a notification from the other process */ op[0].sem_num = semIndex; op[0].sem_op = -1; op[0].sem_flg = 0; if (semop(shm->semid, op, 1) == -1) { log_syserr("semop() failure"); status = ECANCELED; } /* Reacquire the lock */ if (shmfifo_lock(shm) != 0) { status = ECANCELED; } /* lock reacquired */ } /* lock released */ } /* valid "semIndex" */ return status; }
int main( const int argc, const char* const * argv) { int exitCode = 1; if (log_init(argv[0])) { log_syserr("Couldn't initialize logging module"); exitCode = EXIT_FAILURE; } else { if (CUE_SUCCESS == CU_initialize_registry()) { CU_Suite* testSuite = CU_add_suite(__FILE__, setup, teardown); if (NULL != testSuite) { if (CU_ADD_TEST(testSuite, test_socketpair)) { CU_basic_set_mode(CU_BRM_VERBOSE); (void) CU_basic_run_tests(); } } exitCode = CU_get_number_of_tests_failed(); CU_cleanup_registry(); } log_fini(); } return exitCode; }
/* * Returns: * 0 Success. * ENOENT gethostbyname() failure. * EAFNOSUPPORT AF_INET address-family not supported. * EMFILE No more file descriptors available for this process. * ENFILE No more file descriptors available for the system. * EACCES The process does not have appropriate privileges. * ENOBUFS Insufficient resources were available in the system to * perform the operation. * ENOMEM Insufficient memory was available to fulfill the request. * ENOSR There were insufficient STREAMS resources available for the * operation to complete. * EADDRNOTAVAIL The specified address is not available from the local * machine. * ECONNREFUSED The target address was not listening for connections or * refused the connection request. * EINTR The attempt to establish a connection was interrupted by * delivery of a signal that was caught; the connection will be * established asynchronously. * ENETUNREACH No route to the network is present. * EPROTOTYPE The specified address has a different type than the socket * bound to the specified peer address. * ETIMEDOUT The attempt to connect timed out before a connection was * made. * ECONNRESET Remote host reset the connection request. * EHOSTUNREACH The destination host cannot be reached (probably because the * host is down or a remote router cannot reach it). * ENETDOWN The local interface used to reach the destination is down. */ int port_open(const char *remote, unsigned short port, int *const fdp) { int status = ENOERR; int sock; struct sockaddr_in addr; if (addrbyhost(remote, &addr) != 0) { status = ENOENT; log_syserr("gethostbyname(%s)", remote); return status; } sock = socket(AF_INET, SOCK_STREAM, 0); if(sock < 0) { status = errno; log_syserr("socket"); return status; } #if 0 /* doesnt seem to help. See VOODOO in pqing.c */ { const int optval = 1; if(setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char *) &optval, (int) sizeof(optval)) < 0) { status = errno; log_syserr("setsockopt SO_KEEPALIVE"); return status; } } #endif addr.sin_port = htons(port); if(connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { status = errno; log_syserr("connect"); (void) close(sock); return status; } log_notice("NET \"%s\" %hu", remote, port); *fdp = sock; return status; }
/* * Opens the backend database. * * ARGUMENTS: * backend Pointer to pointer to backend structure. Shall not be * NULL. Upon successful return, "*backend" will be set. * The client should call "beClose(*backend)" when the * backend is no longer needed. * dir Pathname of the parent directory of the database. * Shall not be NULL. The client can free it upon return. * forWriting Open the database for writing? 0 <=> no * RETURNS: * 0 Success. "*backend" is set. * ENOMEM System error. "log_add()" called. * EIO Backend database error. "log_add()" called. */ RegStatus beOpen( Backend** const backend, const char* const dir, int forWriting) { RegStatus status; Backend* back = (Backend*)malloc(sizeof(Backend)); assert(NULL != dir); if (NULL == back) { log_syserr("Couldn't allocate %lu bytes", (long)sizeof(Backend)); status = ENOMEM; } else { DB_ENV* env; DB* db; StringBuf* path; if (0 == (status = sb_new(&path, PATH_MAX))) { if (0 == (status = sb_cat(path, dir, "/", DB_DIRNAME))) { if (0 == (status = createDbHandle(path, &env, &db))) { if (status = db->open(db, NULL, DB_FILENAME, NULL, DB_BTREE, forWriting ? DB_CREATE : DB_RDONLY, 0)) { log_add("Couldn't open database \"%s\" in \"%s\" " "for %s", DB_FILENAME, path, forWriting ? "writing" : "reading"); status = EIO; } else { back->db = db; back->cursor.dbCursor = NULL; *backend = back; /* success */ } /* "db" opened */ /* * According to the documentation on DB->open(), if that * call fails, then DB->close() must be called to discard * the DB handle, so DB->close() is the termination * counterpart of db_create() rather than of DB->open(). */ if (status) { (void)db->close(db, 0); (void)env->close(env, 0); } } /* "env" allocated */ } /* DB directory pathname created */ sb_free(path); } /* "path" allocated */ if (status) free(back); } /* "back" allocated */ return status; }
/** * Returns a new file-reader. * * This function is thread-safe. * * @retval 0 Success. * @retval 1 Precondition failure. \c log_add() called. * @retval 2 O/S failure. \c log_add() called. */ int fileReaderNew( const char* const pathname, /**< [in] Pathname of file to read 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 */ { int status = 0; /* default success */ int fd; /* input file descriptor */ bool isStandardInput = NULL == pathname; if (isStandardInput) { if ((fd = fileno(stdin)) == -1) { log_syserr( "Couldn't get file-descriptor of standard input stream"); status = 1; } } else { if ((fd = open(pathname, O_RDONLY)) == -1) { log_syserr("Couldn't open file \"%s\"", pathname); status = 1; } } if (0 == status) { if ((status = readerNew(fd, fifo, sysconf(_SC_PAGESIZE), reader)) != 0) { log_add("Couldn't create new reader object"); if (!isStandardInput) (void)close(fd); } } return status; }
static void* read_from_fd(void* arg) { int fd = *(int*)arg; //for (int n = 1; n <= sizeof(buf); n <<= 1) { for (;;) { int nbytes = read(fd, buf, sizeof(buf)); if (nbytes <= 0) { log_syserr("read() failure"); break; } (void)printf("Read %d bytes\n", nbytes); } return NULL; }
/** * Returns an allocated, shared-memory structure. The returned structure is * unset: it doesn't reference a shared-memory FIFO. The client should call * \link shmfifo_free() \endlink when the structure is no longer needed. * * @retval !NULL Pointer to an allocated shared-memory structure. * @retval NULL Failure. An error message is logged. */ struct shmhandle* shmfifo_new(void) { struct shmhandle* shm = (struct shmhandle*)malloc(sizeof(struct shmhandle)); if (NULL == shm) { log_syserr("Couldn't allocate %lu bytes", sizeof(struct shmhandle)); } else { (void)memset(shm, 0, sizeof(*shm)); shm->mem = NULL; /* necessary because shmfifo_attach() checks */ } return shm; }
/** * There is data available on the feed. Read it into the buffer * then deal with what we got. * * @param ifd [in] File-descriptor of the input data-feed * @retval 0 Success * @retval ENOMEM Out of memory * @retval ENODATA End of input data-feed */ int feedTheXbuf(const int ifd) { int status; size_t nn = 0; /* space available in buffer */ ptrdiff_t remaining = (ptrdiff_t)theBuf->bufsiz - (theBuf->get - theBuf->base); if (remaining <= CHUNKSIZE) { if (theBuf->bufsiz >= maxProductSize) { log_warning( "Data-product would exceed %lu bytes. Resetting input buffer.", maxProductSize); justify_xbuf(theBuf, 0); } log_info("Expanding input buffer size to %lu\n", (unsigned long)(2 * theBuf->bufsiz)); theBuf = expand_xbuf(theBuf, theBuf->bufsiz); if (theBuf == NULL) { status = errno == 0 ? ENOMEM : errno; log_syserr("expand_xbuf"); return status; } } status = (*read_feed)(ifd, (char *)theBuf->put, CHUNKSIZE, &nn); if(status != ENOERR) { log_errno(status, "read_feed"); return status; } /* else */ if(nn == 0) return ENODATA; /* end of file */ /* else */ /* usual case */ /* assert(nn > 0); */ theBuf->cnt += nn; theBuf->put += nn; return ENOERR; }
/* * Creates a database environment handle * * ARGUMENTS: * path Pathname of the database directory. Shall not be NULL. * The client can free it upon return. * dbEnv Pointer to a pointer to the database environment. Shall * not be NULL. "*dbEnv" is set upon successful return. * RETURNS: * 0 Success. "*dbEnv" is set. * ENOMEM System error. "log_add()" called. * EIO Backend database error. "log_add()" called. */ static RegStatus createEnvHandle( const char* const path, DB_ENV** const dbEnv) { RegStatus status; DB_ENV* env; assert(NULL != path); log_list_clear(); if (status = db_env_create(&env, 0)) { log_syserr("Couldn't create environment handle for database: %s", db_strerror(status)); status = ENOMEM; } else { env->set_errcall(env, logDbError); if (status = env->set_isalive(env, is_alive)) { log_add("Couldn't register \"is_alive()\" function for " "database \"%s\"", path); status = EIO; } else { static const unsigned threadCount = 256; if (status = env->set_thread_count(env, threadCount)) { log_add("Couldn't set thread count to %u for database \"%s\"", threadCount, path); status = EIO; } else { *dbEnv = env; } } if (status) (void)env->close(env, 0); } /* "env" allocated */ return status; }
/* * Constructs a path name for the database. * * Arguments: * path Pathname of the database directory. Shall not be NULL. * The client can free it upon return. * ext Extension for the database path name. * Returns: * NULL System error. "log_add()" called. * else Pointer to the path name of the database. The * client should call "free()" when the path name is no * longer needed. */ static char* makeDatabasePath( const char* const path, const char* const ext) { static const size_t lenFilename = sizeof(DB_FILENAME) - 1; size_t lenPath = strlen(path); size_t len = strlen(path) + 1 + lenFilename + strlen(ext) + 1; char* buf = (char*)malloc(len); if (NULL == buf) { log_syserr("Couldn't allocate %lu bytes", (unsigned long)len); } else { (void)strcpy(strcpy(strcpy(strcpy(buf, path) + lenPath, "/") + 1, DB_FILENAME) + lenFilename, ext); } return buf; }
/** * Attaches a data-structure to its shared-memory FIFO. * * @retval 1 Success. * @retval -1 The data-structure is already attached to a shared- * memory FIFO. An error message is logged. * @retval -1 The shared-memory FIFO reference by \e shm couldn't be * attached. An error message is logged. */ int shmfifo_attach( struct shmhandle* const shm) /**< Pointer to the data-structure. */ { void* mem; if (shm->mem) { log_error ("attempt to attach already attached mem?\n"); return -1; } if ((mem = shmat(shm->sid, 0, 0)) == (void*)-1) { log_syserr("Couldn't attach to shared-memory: sid=%d", shm->sid); return -1; } shm->mem = mem; return 1; }
/** * Initializes this module. * * @param readfunct [in] The function that reads the data * @param maxProdSize [in] The size, in bytes, of the largest expected * data-product * @retval 0 Success * @retval ENOMEM Out of memory */ int initTheXbuf( int (*readfunct)(int ifd, char *buf, size_t nbytes, size_t *ngotp), const unsigned long maxProdSize) { read_feed = readfunct; maxProductSize = maxProdSize > INIT_CIRCBUFSIZE ? maxProdSize : INIT_CIRCBUFSIZE; if(theBuf == NULL) { theBuf = new_xbuf(INIT_CIRCBUFSIZE); if(theBuf == NULL) { const int status = errno == 0 ? ENOMEM : errno; log_syserr("new_xbuf"); return status; } } return ENOERR; }
/* * Notifies the reader or writer process. * * Arguments: * shm Pointer to the shared-memory FIFO data-structure. * semIndex Which process to notify. One of SI_WRITER or SI_READER. * Returns: * 0 Success * ECANCELED Operating-system failure. Error-message logged. * EINVAL The FIFO isn't locked by the current process. * Error-message logged. * EINVAL "which" isn't SI_WRITER or SI_READER. Error-message * logged. * Precondition: * The FIFO is locked by the current process. */ static int shmfifo_notify( const struct shmhandle* shm, const SemIndex which) { int status = checkLocked(shm); if (0 == status) { if ((status = vetSemIndex(which)) == 0) { if (semctl(shm->semid, which, SETVAL, 1)) { log_syserr("semctl() failure"); status = ECANCELED; } else { status = 0; } } } return status; }
/** * Forks the current process in the context of the LDM. Does whatever's * necessary before and after the fork to ensure correct behavior. Terminates * the child process if the fork() was successful but an error occurs. * * @retval -1 Failure. "log_add()" called. * @retval 0 Success. The calling process is the child. * @return PID of child process. The calling process is the parent. */ pid_t ldmfork(void) { pid_t pid; if (reg_close()) { pid = -1; } else { pid = fork(); if (0 == pid) { log_clear(); // So child process starts with no queued messages /* Child process */ } else if (-1 == pid) { /* System error */ log_syserr("Couldn't fork a child process"); } } return pid; }
/* * Sets the cursor to reference the first entry in the backend * database whose key is greater than or equal to a given key. * * ARGUMENTS: * backend Pointer to the backend database. Shall have been * set by beOpen(). Shall not be NULL. * key Pointer to the starting key. Shall not be NULL. The * empty string obtains the first entry in the database, * if it exists. * RETURNS * 0 Success. * EINVAL The cursor is not initialized. * ENOENT The database is empty. * EIO Backend database error. "log_add()" called. * ENOMEM System error. "log_add()" called. */ RegStatus beFirstEntry( Backend* const backend, const char* const key) { RegStatus status; Cursor* cursor; assert(NULL != backend); assert(NULL != key); cursor = &backend->cursor; if (!cursor->dbCursor) { log_add("Cursor for backend database \"%s\" not initialized", getPath(backend->db)); status = EINVAL; } else { char* const dupKey = strdup(key); if (NULL == dupKey) { log_syserr("Couldn't allocate %lu bytes", (long)strlen(key)); status = ENOMEM; } else { backend->cursor.key.data = dupKey; backend->cursor.key.size = strlen(dupKey) + 1; if (EIO == (status = setCursor(&backend->cursor, DB_SET_RANGE))) { log_add("Couldn't set cursor for database \"%s\" to first " "entry on or after key \"%s\"", getPath(backend->db), key); } } /* "dupKey" allocated */ } return status; }
/** * Returns a data-structure for accessing a shared-memory FIFO. Creates the * FIFO is it doesn't already exist. * * @retval !NULL Pointer the data-structure for accessing the * shared-memory FIFO. * @retval NULL Failure. An error message is logged. */ struct shmhandle* shmfifo_create( const int npages, /**< size of the FIFO in pages */ const int privsz, /**< <size of the private portion of the FIFO in bytes */ const int nkey) /**< Partial key associated with the FIFO or \c -1 to obtain a private, shared-memory FIFO. */ { int shmSize = npages*getpagesize(); int shmid; struct shmhandle* shm = NULL; /* default failure */ key_t key; if (nkey == -1) { shmid = shmget(IPC_PRIVATE, shmSize, IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR); } else { key = (key_t)(DVBS_ID + nkey); /* * IPC_EXCL creates an error condition if the memory already exists... * we can use the existing memory if the program has not changed the * size of the segment or the private structure size */ shmid = shmget(key, shmSize, IPC_CREAT | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); } if (shmid == -1) { log_syserr("shmget() failure: npages=%d, nkey=%d", npages, nkey); } else { /* Temporarily attach to initialize the control structure. */ struct shmprefix* p = (struct shmprefix*)shmat(shmid, 0, 0); if (p == (void*)-1) { log_syserr("shmat() failure: id=%d", shmid); } else { int semid; p->read = p->write = sizeof(struct shmprefix) + privsz; p->sz = shmSize; p->privsz = privsz; (void)memset((char*)p + sizeof(struct shmprefix), 0, privsz); (void)shmdt(p); p = NULL; /* Get semaphore */ if (nkey == -1) { semid = semget(IPC_PRIVATE, SI_SEM_COUNT, IPC_CREAT | IPC_EXCL + 0600); } else { /* * IPC_EXCL not used in order to get existing semaphore if * possible. */ semid = semget(key, SI_SEM_COUNT, IPC_CREAT + 0660); } if (semid == -1) { log_syserr("semget() failure"); } else { unsigned short values[SI_SEM_COUNT]; union semun arg; log_debug("shmfifo_create(): Got semaphore: pid=%d, semid=%d", getpid(), semid); values[SI_LOCK] = 1; values[SI_WRITER] = 0; values[SI_READER] = 0; arg.array = values; if (semctl(semid, 0, SETALL, arg) == -1) { log_syserr("semctl() failure: semid=%d", semid); } else { shm = shmfifo_new(); if (NULL != shm) { shm->sid = shmid; shm->privsz = privsz; shm->sz = shmSize; shm->semid = semid; } } /* semaphore values set */ } /* got semaphore set */ } /* shared-memory was attached to "p" */ } /* got shared-memory segment ID */ return shm; }
int main (int ac, char *av[]) { char *progname = av[0]; int status; int seq_start = 0; stat_info *sinfo, *shead = NULL, *slast = NULL; int statusoff=0; /* * Set up error logging */ if (log_init(progname)) { log_syserr("Couldn't initialize logging module"); exit(1); } const char* pqfname = getQueuePath(); /* * Check the environment for some options. * May be overridden by command line switches below. */ { const char *ldmpqfname = getenv ("LDMPQFNAME"); if (ldmpqfname != NULL) pqfname = ldmpqfname; } { extern int optind; extern int opterr; extern char *optarg; int ch; opterr = 1; while ((ch = getopt (ac, av, "vxl:q:f:s: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': if (log_set_destination(optarg)) { log_syserr("Couldn't set logging destination to \"%s\"", optarg); exit(1); } break; case 'q': pqfname = 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 'S': statusoff=1; break; case '?': usage (progname); break; } setQueuePath(pqfname); ac -= optind; av += optind; if (ac < 1) usage (progname); } /* * register exit handler */ if (atexit (cleanup) != 0) { log_syserr ("atexit"); exit (1); } /* * set up signal handlers */ set_sigactions (); /* * who am i, anyway */ (void) strcpy (myname, ghostname ()); /* * open the product queue */ if ((status = pq_open (pqfname, PQ_DEFAULT, &pq))) { if (status > 0) { log_add_syserr("\"%s\" failed", pqfname); log_flush_error(); } else { log_error_q("\"%s\" failed: %s", pqfname, "Internal error"); } exit (2); } { char *filename; int fd; struct stat statb; product prod; unsigned char *prodmmap; MD5_CTX *md5ctxp = NULL; int gversion; /* * Allocate an MD5 context */ md5ctxp = new_MD5_CTX (); if (md5ctxp == NULL) { log_syserr ("new_md5_CTX failed"); exit (6); } /* These members are constant over the loop. */ prod.info.origin = myname; prod.info.feedtype = feedtype; prod.info.seqno = seq_start; /* * Open the file to be inserted and process */ while (ac > 0) { long insert_sum = 0; long sinfo_cnt = 0; long stat_size = 0; filename = *av; av++; ac--; log_notice_q ("open and memorymap %s\0", filename); fd = open (filename, O_RDONLY, 0); if (fd == -1) { log_syserr ("open: %s", filename); continue; } if (fstat (fd, &statb) == -1) { log_syserr ("fstat: %s", filename); (void) close (fd); continue; } if ((prodmmap = (unsigned char *) mmap (0, statb.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) { log_syserr ("allocation failed"); } else { int GRIBDONE = 0; off_t griboff = 0; size_t griblen = 0; log_notice_q ("%ld bytes memory mapped\0", (long) statb.st_size); while (!GRIBDONE) { log_debug("griboff %d\0", (int) griboff); /* get offset of next grib product */ status = get_grib_info (prodmmap, statb.st_size, &griboff, &griblen, &gversion); switch (status) { case 0: prod.data = prodmmap + griboff; prod.info.sz = griblen; /* * revised MD5 calculation...using filename * to allow duplicate products in different files. */ MD5Init (md5ctxp); MD5Update (md5ctxp, (void *)filename, strlen(filename)); /*MD5Update (md5ctxp, (void *)prod.data, prod.info.sz);*/ if ( prod.info.sz > 10000 ) MD5Update (md5ctxp, (void *)prod.data, 10000); else MD5Update (md5ctxp, (void *)prod.data, prod.info.sz); MD5Final (prod.info.signature, md5ctxp); /*if (mm_md5 (md5ctxp, prod.data, prod.info.sz, prod.info.signature) != 0) { log_error_q ("could not compute MD5\0"); } else { */ prod.info.ident = (char *) malloc (KEYSIZE + 1); get_gribname (gversion, prod.data, prod.info.sz, filename, prod.info.seqno, prod.info.ident); /* * Do the deed */ status = set_timestamp (&prod.info.arrival); if (status != ENOERR) { log_add_syserr("could not set timestamp"); log_flush_error(); } /* * Insert the product */ status = pq_insert (pq, &prod); log_info_q ("%d %s\0", status, prod.info.ident); if ( status == ENOERR ) insert_sum += prod.info.sz; if (! statusoff ) { /* * Log this status */ sinfo_cnt++; sinfo = (stat_info *)malloc(sizeof(stat_info)); sinfo->insertstatus = status; sinfo->prodname = (char *)malloc(strlen(prod.info.ident)+1); strcpy(sinfo->prodname, prod.info.ident); sinfo->seqno = prod.info.seqno; sinfo->prodsz = prod.info.sz; sinfo->next = NULL; stat_size += strlen(sinfo->prodname); if(shead == NULL) { shead = sinfo; slast = sinfo; } else { slast->next = sinfo; slast = sinfo; } } /*}*/ griboff += griblen; prod.info.seqno++; break; case -1: GRIBDONE = 1; break; case -2: log_error_q ("truncated grib file at: %d", prod.info.seqno); GRIBDONE = 1; break; case -7: log_error_q ("End sequence 7777 not found where expected: %d", prod.info.seqno); griboff += griblen; log_error_q("resume looking at %d\0",griboff); break; default: log_error_q ("unknown error %d\0", status); griboff += griblen; if (griboff >= statb.st_size) GRIBDONE = 1; break; } if (griboff >= statb.st_size) GRIBDONE = 1; } log_notice_q ("munmap\0"); (void) munmap ((void *)prodmmap, statb.st_size); if ( stat_size != 0 ) /* * Add a status message to product queue */ { char *statusmess; log_notice_q("stats_size %ld %ld\0",stat_size,sinfo_cnt); statusmess = calloc((30 * sinfo_cnt) + stat_size + strlen(filename) + 128, sizeof(char)); if(statusmess == NULL) { log_syserr("could not malloc status message %ld\0", stat_size); } else { char tmpprod[512]; sinfo = shead; slast = NULL; status = set_timestamp(&prod.info.arrival); /* ctime ends with \n\0" */ sprintf(statusmess,"%s complete (%ld bytes) at %sInserted %ld of %ld\n", filename,(long)statb.st_size, ctime(&prod.info.arrival.tv_sec), insert_sum,(long)statb.st_size); while(sinfo != NULL) { sprintf(tmpprod,"%3d %5d %8d %s\n",sinfo->insertstatus, sinfo->seqno,sinfo->prodsz,sinfo->prodname); strcat(statusmess,tmpprod); slast = sinfo; sinfo = sinfo->next; free(slast->prodname); free(slast); } shead = NULL; sprintf(tmpprod,".status.%s %06d",filename, prod.info.seqno); prod.info.ident = tmpprod; prod.data = statusmess; prod.info.sz = strlen(statusmess); status = mm_md5(md5ctxp, prod.data, prod.info.sz, prod.info.signature); status = set_timestamp(&prod.info.arrival); status = pq_insert(pq, &prod); if(log_is_enabled_info) log_info_q("%s", s_prod_info(NULL, 0, &prod.info, log_is_enabled_debug)) ; free(statusmess); prod.info.seqno++; } } } (void) close (fd); } } exit(0); }
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 = basename(av[0]); unsigned long maxProductSize = DEFAULT_MAX_PRODUCT_SIZE; /* * Setup default logging before anything else. */ (void)log_init(progname); 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; while ((ch = getopt(ac, av, ":vxcni5Nl:b:p:P:T:q:r:f: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 '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': { (void)log_set_destination(optarg); 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_error("Invalid server port: \"%s\"", optarg); usage(progname); } break; case 'T': reset_secs = atoi(optarg); if(reset_secs < 0) { log_add("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_error("Invalid maximum data-product size: \"%s\"", optarg); usage(progname); } maxProductSize = size; break; } case 'q': setQueuePath(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_add("Unknown option: \"%c\"", optopt); usage(progname); break; } case ':': /*FALLTHROUGH*/ default: log_add("Missing argument for option: \"%c\"", optopt); usage(progname); break; } /* last arg, feedfname, is required */ if(ac - optind != 1) { log_add("Wrong number of operands: %d", ac - optind); usage(progname); } (void)strncat(feedfname, av[optind], sizeof(feedfname)-6); } pqpath = getQueuePath(); log_notice("Starting Up"); log_debug(PACKAGE_VERSION); /* * register exit handler */ if(atexit(cleanup) != 0) { log_syserr("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) { log_error("The product-queue \"%s\" is inconsistent\n", pqpath); } else { log_error("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; log_info("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 */ log_notice("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) { log_syserr("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) { log_notice("Statistics Request"); if(pq != NULL) { off_t highwater = 0; size_t maxregions = 0; (void) pq_highwater(pq, &highwater, &maxregions); log_notice(" Queue usage (bytes):%8ld", (long)highwater); log_notice(" (nregions):%8ld", (long)maxregions); } log_notice(" Idle: %8lu seconds", idle); #if NET if (INPUT_IS_SOCKET) { log_notice(" Timeout: %8d", reset_secs); } #endif log_notice("%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) { log_error ("maximum retry attempts %d, aborting", MAX_RETRIES); done = !0; continue; } /* Try to reopen on tcp read errors */ log_notice("Trying to re-open connection on port %d", server_port); ++retries; if(open_feed(feedfname, &ifd, maxProductSize) != ENOERR) { log_notice ("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; } log_syserr("select"); return 1; } /* else */ #if 0 if (FD_ISSET(ifd, &exceptfds)) { log_error("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 { log_error("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)) { log_notice("Idle for %ld seconds, reconnecting", idle); /* force reconnect */ port_error = !0; idle = 0; continue; } } #endif /* NET */ (void) scanTheXbuf(); } return 0; }
int main(int ac, char *av[]) { unsigned timeo = DEFAULT_TIMEO; unsigned interval = DEFAULT_TIMEO; unsigned TotalTimeo = DEFAULT_TOTALTIMEO; prod_spec spec; int status; prod_class_t* clssp; /* * initialize logger */ if (log_init(av[0])) { log_syserr("Couldn't initialize logging module"); exit(1); } if(set_timestamp(&clss.from) != 0) { fprintf(stderr, "Couldn't set timestamp\n"); exit(1); } clss.to = TS_ENDT; clss.psa.psa_len = 1; clss.psa.psa_val = &spec; spec.feedtype = DEFAULT_FEEDTYPE; spec.pattern = DEFAULT_PATTERN; { /* Begin getopt block */ extern int optind; extern int opterr; extern char *optarg; int ch; int fterr; opterr = 1; while ((ch = getopt(ac, av, "vxl:f:o:t:h:P:p:T:")) != EOF) switch (ch) { case 'v': if (!log_is_enabled_info) log_set_level(LOG_LEVEL_INFO); break; case 'x': log_set_level(LOG_LEVEL_DEBUG); break; case 'l': if (log_set_destination(optarg)) { log_syserr("Couldn't set logging destination to \"%s\"", optarg); usage(av[0]); } break; case 'h': remote = optarg; break; case 'P': { log_warning("Port specification is ignored"); break; } case 'p': spec.pattern = optarg; /* compiled below */ break; case 'f': fterr = strfeedtypet(optarg, &spec.feedtype); if(fterr != FEEDTYPE_OK) { fprintf(stderr, "Bad feedtype \"%s\", %s\n", optarg, strfeederr(fterr)); usage(av[0]); } break; case 'o': clss.from.tv_sec -= atoi(optarg); break; case 'T': TotalTimeo = atoi(optarg); if(TotalTimeo == 0) { fprintf(stderr, "%s: invalid TotalTimeo %s", av[0], optarg); usage(av[0]); } break; case 't': timeo = (unsigned)atoi(optarg); if(timeo == 0 || timeo > 32767) { fprintf(stderr, "%s: invalid timeout %s", av[0], optarg); usage(av[0]); } break; case '?': usage(av[0]); break; } if(ac - optind > 0) usage(av[0]); if (re_isPathological(spec.pattern)) { fprintf(stderr, "Adjusting pathological regular-expression: " "\"%s\"\n", spec.pattern); re_vetSpec(spec.pattern); } status = regcomp(&spec.rgx, spec.pattern, REG_EXTENDED|REG_NOSUB); if(status != 0) { fprintf(stderr, "Bad regular expression \"%s\"\n", spec.pattern); usage(av[0]); } if(TotalTimeo < timeo) { fprintf(stderr, "TotalTimeo %u < timeo %u\n", TotalTimeo, timeo); usage(av[0]); } } /* End getopt block */ log_notice_q("Starting Up: %s: %s", remote, s_prod_class(NULL, 0, &clss)); /* * register exit handler */ if(atexit(cleanup) != 0) { log_syserr("atexit"); exit(1); } /* * set up signal handlers */ set_sigactions(); /* * Try forever. */ while (exitIfDone(0)) { clssp = &clss; status = forn5(NOTIFYME, remote, &clssp, timeo, TotalTimeo, notifymeprog_5); (void)exitIfDone(0); switch(status) { /* problems with remote, retry */ case ECONNABORTED: case ECONNRESET: case ETIMEDOUT: case ECONNREFUSED: sleep(interval); break; case 0: /* assert(done); */ break; default: /* some wierd error */ done = 1; exit(1); } } exit(0); /*NOTREACHED*/ }
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("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("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("invalid offset %s\n", optarg); usage(progname); } break; case 'i': interval = atoi(optarg); if(interval == 0 && *optarg != '0') { log_error("invalid interval %s\n", optarg); usage(progname); } break; case 't': pipe_timeo = atoi(optarg); if(pipe_timeo == 0 && *optarg != 0) { log_error("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("Too many operands"); usage(progname); } else if (1 == numOperands) { conffilename = av[optind]; } } } setQueuePath(pqfname); log_notice("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("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("Couldn't duplicate string \"%s\"", buf); exit(EXIT_FAILURE); } } /* * Initialize the previous-state module for this process. */ if (stateInit(conffilename) < 0) { log_error("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("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("Couldn't set number of available file-descriptors"); log_notice("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("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("Can't compile regular expression \"%s\"", spec.pattern); log_notice("Exiting"); exit(EXIT_FAILURE); /*NOTREACHED*/ } /* * register exit handler */ if(atexit(cleanup) != 0) { log_syserr("atexit"); log_notice("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("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("The product-queue \"%s\" is inconsistent\n", pqfname); } else { log_error("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("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("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("Starting from insertion-time %s.%06lu UTC", buf, (unsigned long)insertTime.tv_usec); pq_cset(pq, &insertTime); startAtTailEnd = false; } } if (startAtTailEnd) { log_notice("Starting at tail-end of product-queue"); (void)pq_last(pq, &clss, NULL); } }