/* retry registering inotify watches, using the retry strategy parameters */ static void wait_reg_watches(const char *qpath, const char *rqpath) { double slp = WAIT_INIT; while (!stop_requested()) { if (try_reg_watches(qpath, rqpath)) { flog(LOG_DEBUG, "registered watches"); break; } else { sleepsec(slp); slp = (slp * WAIT_MULT); if (slp > WAIT_MAX) slp = WAIT_MAX; } } }
/* exec run_loop for all correct entries in (r)queue directory NOT thread-safe, since readdir_r is unreliable with filenames > NAME_MAX (e.g., NTFS + 255 unicode chars get truncated to 256 chars w/o terminating NUL) */ static void retry_dir(const char *qtype, const char *qpath, const char *looppath) { /* [offsetof(struct dirent, d_name) + fpathconf(fd, _PC_NAME_MAX) + 1] */ struct dirent *de; struct stat st; DIR *qdir; int fd, run; flog(LOG_DEBUG, "retrying %s directories", qtype); /* open directory (O_CLOEXEC is implied) */ if ((qdir = opendir(qpath))) { /* get corresponding file descriptor for stat */ if ((fd = dirfd(qdir)) != -1) { for (errno = 0; !stop_requested() && ((de = readdir(qdir))); ) { run = 0; /* some filesystems don't support d_type, need to stat entry */ if (de->d_type == DT_UNKNOWN && is_msgdir(de->d_name)) { if (!fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW)) run = S_ISDIR(st.st_mode); else warning("fstat failed"); } else run = (de->d_type == DT_DIR && is_msgdir(de->d_name)); if (run) run_loop(qtype, de->d_name, looppath); } if (errno && errno != EINTR) warning("reading directory failed"); } else warning("dirfd failed"); /* close directory */ if (closedir(qdir)) warning("could not close directory"); } else warning("could not open directory"); }
bool play_internal(wav_t *w, audio_t *audio, talking_skull_t *talking_skull, stop_t *stop) { size_t size; size_t i; bool rc = true; unsigned handle; assert(w); assert(! talking_skull || ! stop); size = audio_get_buffer_size(audio); if (talking_skull) { if (w->servo) { handle = talking_skull_play(talking_skull, w->servo, w->n_servo); } else { handle = talking_skull_play(talking_skull, w->audio, w->n_audio); } } for (i = 0; i < w->n_audio && ! stop_requested(stop); i += size) { size_t this_size = i + size > w->n_audio ? w->n_audio - i : size; if (! audio_play_buffer(audio, &w->audio[i], this_size)) { perror("Error playing sample"); rc = false; break; } } if (talking_skull) { talking_skull_wait_completion(talking_skull, handle); } stop_stopped(stop); return rc; }
int main() { /* using NAME_MAX prevents EINVAL on read() (twice for UTF-16 on NTFS) */ char buf[sizeof(struct inotify_event) + NAME_MAX*2 + 1]; char *crtpath, *qpath, *rqpath, *looppath, *lsthost, *lstport; int sz, offset, rereg, evqok, retryid; struct inotify_event *iev; double retrytmout, lastclock; /* init logging */ syslog_init(); /* extract environment */ crtpath = alloc_env(CABLE_CERTS, "/" CERTS_NAME); qpath = alloc_env(CABLE_QUEUES, "/" QUEUE_NAME); rqpath = alloc_env(CABLE_QUEUES, "/" RQUEUE_NAME); looppath = alloc_env(CABLE_HOME, "/" LOOP_NAME); lsthost = alloc_env(CABLE_HOST, ""); lstport = alloc_env(CABLE_PORT, ""); /* initialize rng */ if (!rand_init()) warning("failed to initialize RNG"); /* initialize process accounting */ if (!init_process_acc()) warning("failed to initialize process accounting"); /* initialize webserver */ if (!init_server(crtpath, qpath, rqpath, lsthost, lstport)) { flog(LOG_ERR, "failed to initialize webserver"); return EXIT_FAILURE; } /* try to reregister watches as long as no signal caught */ for (lastclock = getmontime(), retryid = 0; !stop_requested(); ) { /* support empty CABLE_NOLOOP when testing, to act as pure server */ #ifdef TESTING if (getenv("CABLE_NOLOOP")) { sleepsec(RETRY_TMOUT); continue; } #endif wait_reg_watches(qpath, rqpath); /* read events as long as no signal caught and no unmount / move_self / etc. events read */ for (rereg = evqok = 0; !stop_requested() && !rereg; ) { /* wait for an event, or timeout (later blocking read() results in error) */ retrytmout = RETRY_TMOUT + RETRY_TMOUT * (rand_shift() / 2); if (wait_read(inotfd, retrytmout - (getmontime() - lastclock))) { /* read events (non-blocking), taking care to handle interrupts due to signals */ if ((sz = read(inotfd, buf, sizeof(buf))) == -1 && errno != EINTR) { /* happens buffer is too small (e.g., NTFS + 255 unicode chars) */ warning("error while reading from inotify queue"); rereg = 1; } /* process all events in buffer, sz = -1 and 0 are automatically ignored */ for (offset = 0; offset < sz && !stop_requested() && !rereg; evqok = 1) { /* get handler to next event in read buffer, and update offset */ iev = (struct inotify_event*) (buf + offset); offset += sizeof(struct inotify_event) + iev->len; /* IN_IGNORED is triggered by watched directory removal / fs unmount IN_MOVE_SELF is only triggered by move of actual watched directory (i.e., not its parent) */ if ((iev->mask & (IN_IGNORED | IN_UNMOUNT | IN_Q_OVERFLOW | IN_MOVE_SELF))) rereg = 1; /* ignore non-subdirectory events, and events with incorrect name */ else if (iev->len > 0 && (iev->mask & IN_ISDIR) && is_msgdir(iev->name)) { assert(iev->wd == inotqwd || iev->wd == inotrqwd); if (iev->wd == inotqwd || iev->wd == inotrqwd) { /* stop can be indicated here (while waiting for less processes) */ const char *qtype = (iev->wd == inotqwd) ? QUEUE_NAME : RQUEUE_NAME; run_loop(qtype, iev->name, looppath); } else flog(LOG_WARNING, "unknown watch descriptor"); } } } /* if sufficient time passed since last retries, retry again */ if (!stop_requested() && getmontime() - lastclock >= retrytmout) { /* alternate between queue dirs to prevent lock starvation on self-send */ if ((retryid ^= 1)) retry_dir(QUEUE_NAME, qpath, looppath); else retry_dir(RQUEUE_NAME, rqpath, looppath); lastclock = getmontime(); /* inotify is apparently unreliable on fuse, so reregister when no events */ if (!evqok) rereg = 1; evqok = 0; } } } unreg_watches(); if (!shutdown_server()) flog(LOG_WARNING, "failed to shutdown webserver"); dealloc_env(lstport); dealloc_env(lsthost); dealloc_env(looppath); dealloc_env(rqpath); dealloc_env(qpath); dealloc_env(crtpath); flog(LOG_INFO, "exiting"); closelog(); return EXIT_SUCCESS; }