about summary refs log tree commit homepage
path: root/lib/PublicInbox/xap_helper.h
diff options
context:
space:
mode:
Diffstat (limited to 'lib/PublicInbox/xap_helper.h')
-rw-r--r--lib/PublicInbox/xap_helper.h202
1 files changed, 82 insertions, 120 deletions
diff --git a/lib/PublicInbox/xap_helper.h b/lib/PublicInbox/xap_helper.h
index 8602170f..1380ffd0 100644
--- a/lib/PublicInbox/xap_helper.h
+++ b/lib/PublicInbox/xap_helper.h
@@ -78,6 +78,10 @@
         assert(ckvar______ == (expect) && "BUG" && __FILE__ && __LINE__); \
 } while (0)
 
+// coredump on most usage errors since our only users are internal
+#define ABORT(...) do { warnx(__VA_ARGS__); abort(); } while (0)
+#define EABORT(...) do { warn(__VA_ARGS__); abort(); } while (0)
+
 // sock_fd is modified in signal handler, yes, it's SOCK_SEQPACKET
 static volatile int sock_fd = STDIN_FILENO;
 static sigset_t fullset, workerset;
@@ -145,10 +149,8 @@ struct worker {
 #define SPLIT2ARGV(dst,buf,len) split2argv(dst,buf,len,MY_ARRAY_SIZE(dst))
 static size_t split2argv(char **dst, char *buf, size_t len, size_t limit)
 {
-        if (buf[0] == 0 || len == 0 || buf[len - 1] != 0) {
-                warnx("bogus argument given");
-                return 0;
-        }
+        if (buf[0] == 0 || len == 0 || buf[len - 1] != 0)
+                ABORT("bogus argument given");
         size_t nr = 0;
         char *c = buf;
         for (size_t i = 1; i < len; i++) {
@@ -156,11 +158,11 @@ static size_t split2argv(char **dst, char *buf, size_t len, size_t limit)
                         dst[nr++] = c;
                         c = buf + i + 1;
                 }
-                if (nr == limit) {
-                        warnx("too many args: %zu", nr);
-                        return 0;
-                }
+                if (nr == limit)
+                        ABORT("too many args: %zu == %zu", nr, limit);
         }
+        if (nr == 0) ABORT("no argument given");
+        if ((long)nr < 0) ABORT("too many args: %zu", nr);
         return (long)nr;
 }
 
@@ -242,6 +244,8 @@ static void emit_mset_stats(struct req *req, const Xapian::MSet *mset)
         if (req->fp[1])
                 fprintf(req->fp[1], "mset.size=%llu nr_out=%zu\n",
                         (unsigned long long)mset->size(), req->nr_out);
+        else
+                ABORT("BUG: %s caller only passed 1 FD", req->argv[0]);
 }
 
 static bool starts_with(const std::string *s, const char *pfx, size_t pfx_len)
@@ -290,20 +294,14 @@ static enum exc_iter dump_ibx_iter(struct req *req, const char *ibx_id,
 
 static bool cmd_dump_ibx(struct req *req)
 {
-        if ((optind + 1) >= req->argc) {
-                warnx("usage: dump_ibx [OPTIONS] IBX_ID QRY_STR");
-                return false; // need ibx_id + qry_str
-        }
-        if (!req->pfxc) {
-                warnx("dump_ibx requires -A PREFIX");
-                return false;
-        }
+        if ((optind + 1) >= req->argc)
+                ABORT("usage: dump_ibx [OPTIONS] IBX_ID QRY_STR");
+        if (!req->pfxc)
+                ABORT("dump_ibx requires -A PREFIX");
 
         const char *ibx_id = req->argv[optind];
-        if (my_setlinebuf(req->fp[0])) { // for sort(1) pipe
-                perror("setlinebuf(fp[0])");
-                return false;
-        }
+        if (my_setlinebuf(req->fp[0])) // for sort(1) pipe
+                EABORT("setlinebuf(fp[0])"); // WTF?
         req->asc = true;
         req->sort_col = -1;
         Xapian::MSet mset = mail_mset(req, req->argv[optind + 1]);
@@ -344,24 +342,22 @@ static void fbuf_ensure(void *ptr)
 {
         struct fbuf *fbuf = (struct fbuf *)ptr;
         if (fbuf->fp && fclose(fbuf->fp))
-                perror("fclose(fbuf->fp)");
+                err(EXIT_FAILURE, "fclose(fbuf->fp)"); // ENOMEM?
         fbuf->fp = NULL;
         free(fbuf->ptr);
 }
 
-static bool fbuf_init(struct fbuf *fbuf)
+static void fbuf_init(struct fbuf *fbuf)
 {
         assert(!fbuf->ptr);
         fbuf->fp = open_memstream(&fbuf->ptr, &fbuf->len);
-        if (fbuf->fp) return true;
-        perror("open_memstream(fbuf)");
-        return false;
+        if (!fbuf->fp) err(EXIT_FAILURE, "open_memstream(fbuf)");
 }
 
 static void xclose(int fd)
 {
         if (close(fd) < 0 && errno != EINTR)
-                err(EXIT_FAILURE, "BUG: close");
+                EABORT("BUG: close");
 }
 
 #define CLEANUP_DUMP_ROOTS __attribute__((__cleanup__(dump_roots_ensure)))
@@ -372,19 +368,17 @@ static void dump_roots_ensure(void *ptr)
                 xclose(drt->root2id_fd);
         hdestroy(); // idempotent
         if (drt->mm_ptr && munmap(drt->mm_ptr, drt->sb.st_size))
-                err(EXIT_FAILURE, "BUG: munmap");
+                EABORT("BUG: munmap(%p, %zu)", drt->mm_ptr, drt->sb.st_size);
         free(drt->entries);
         fbuf_ensure(&drt->wbuf);
 }
 
 static bool root2ids_str(struct fbuf *root_ids, Xapian::Document *doc)
 {
-        if (!fbuf_init(root_ids)) return false;
-
-        bool ok = true;
         Xapian::TermIterator cur = doc->termlist_begin();
         Xapian::TermIterator end = doc->termlist_end();
         ENTRY e, *ep;
+        fbuf_init(root_ids);
         for (cur.skip_to("G"); cur != end; cur++) {
                 std::string tn = *cur;
                 if (!starts_with(&tn, "G", 1))
@@ -393,21 +387,16 @@ static bool root2ids_str(struct fbuf *root_ids, Xapian::Document *doc)
                 u.in = tn.c_str() + 1;
                 e.key = u.out;
                 ep = hsearch(e, FIND);
-                if (!ep) {
-                        warnx("hsearch miss `%s'", e.key);
-                        return false;
-                }
+                if (!ep) ABORT("hsearch miss `%s'", e.key);
                 // ep->data is a NUL-terminated string matching /[0-9]+/
                 fputc(' ', root_ids->fp);
                 fputs((const char *)ep->data, root_ids->fp);
         }
         fputc('\n', root_ids->fp);
-        if (ferror(root_ids->fp) | fclose(root_ids->fp)) {
-                perror("ferror|fclose(root_ids)");
-                ok = false;
-        }
+        if (ferror(root_ids->fp) | fclose(root_ids->fp))
+                err(EXIT_FAILURE, "ferror|fclose(root_ids)"); // ENOMEM
         root_ids->fp = NULL;
-        return ok;
+        return true;
 }
 
 // writes term values matching @pfx for a given @doc, ending the line
@@ -440,20 +429,17 @@ static bool dump_roots_flush(struct req *req, struct dump_roots_tmp *drt)
         bool ok = true;
 
         if (!drt->wbuf.fp) return true;
-        if (fd < 0) err(EXIT_FAILURE, "BUG: fileno");
-        if (fclose(drt->wbuf.fp)) {
-                warn("fclose(drt->wbuf.fp)"); // malloc failure?
-                return false;
-        }
+        if (fd < 0) EABORT("BUG: fileno");
+        if (ferror(drt->wbuf.fp) | fclose(drt->wbuf.fp)) // ENOMEM?
+                err(EXIT_FAILURE, "ferror|fclose(drt->wbuf.fp)");
         drt->wbuf.fp = NULL;
         if (!drt->wbuf.len) goto done_free;
         while (flock(drt->root2id_fd, LOCK_EX)) {
                 if (errno == EINTR) continue;
-                perror("LOCK_EX");
-                return false;
+                err(EXIT_FAILURE, "LOCK_EX"); // ENOLCK?
         }
         p = drt->wbuf.ptr;
-        do {
+        do { // write to client FD
                 ssize_t n = write(fd, p, drt->wbuf.len);
                 if (n > 0) {
                         drt->wbuf.len -= n;
@@ -465,10 +451,9 @@ static bool dump_roots_flush(struct req *req, struct dump_roots_tmp *drt)
         } while (drt->wbuf.len);
         while (flock(drt->root2id_fd, LOCK_UN)) {
                 if (errno == EINTR) continue;
-                perror("LOCK_UN");
-                return false;
+                err(EXIT_FAILURE, "LOCK_UN"); // ENOLCK?
         }
-done_free:
+done_free: // OK to skip on errors, dump_roots_ensure calls fbuf_ensure
         free(drt->wbuf.ptr);
         drt->wbuf.ptr = NULL;
         return ok;
@@ -501,7 +486,7 @@ static char *hsearch_enter_key(char *s)
         // hdestroy frees each key on some platforms,
         // so give it something to free:
         char *ret = strdup(s);
-        if (!ret) perror("strdup");
+        if (!ret) err(EXIT_FAILURE, "strdup");
         return ret;
 // AFAIK there's no way to detect musl, assume non-glibc Linux is musl:
 #elif defined(__GLIBC__) || defined(__linux__) || \
@@ -518,61 +503,42 @@ static bool cmd_dump_roots(struct req *req)
 {
         CLEANUP_DUMP_ROOTS struct dump_roots_tmp drt = {};
         drt.root2id_fd = -1;
-        if ((optind + 1) >= req->argc) {
-                warnx("usage: dump_roots [OPTIONS] ROOT2ID_FILE QRY_STR");
-                return false; // need file + qry_str
-        }
-        if (!req->pfxc) {
-                warnx("dump_roots requires -A PREFIX");
-                return false;
-        }
+        if ((optind + 1) >= req->argc)
+                ABORT("usage: dump_roots [OPTIONS] ROOT2ID_FILE QRY_STR");
+        if (!req->pfxc)
+                ABORT("dump_roots requires -A PREFIX");
         const char *root2id_file = req->argv[optind];
         drt.root2id_fd = open(root2id_file, O_RDONLY);
-        if (drt.root2id_fd < 0) {
-                warn("open(%s)", root2id_file);
-                return false;
-        }
-        if (fstat(drt.root2id_fd, &drt.sb)) {
-                warn("fstat(%s)", root2id_file);
-                return false;
-        }
+        if (drt.root2id_fd < 0)
+                EABORT("open(%s)", root2id_file);
+        if (fstat(drt.root2id_fd, &drt.sb)) // ENOMEM?
+                err(EXIT_FAILURE, "fstat(%s)", root2id_file);
         // each entry is at least 43 bytes ({OIDHEX}\0{INT}\0),
         // so /32 overestimates the number of expected entries by
         // ~%25 (as recommended by Linux hcreate(3) manpage)
         size_t est = (drt.sb.st_size / 32) + 1; //+1 for "\0" termination
-        if ((uint64_t)drt.sb.st_size > (uint64_t)SIZE_MAX) {
-                warnx("%s size too big (%lld bytes > %zu)", root2id_file,
-                        (long long)drt.sb.st_size, SIZE_MAX);
-                return false;
-        }
+        if ((uint64_t)drt.sb.st_size > (uint64_t)SIZE_MAX)
+                err(EXIT_FAILURE, "%s size too big (%lld bytes > %zu)",
+                        root2id_file, (long long)drt.sb.st_size, SIZE_MAX);
         drt.mm_ptr = mmap(NULL, drt.sb.st_size, PROT_READ,
                                 MAP_PRIVATE, drt.root2id_fd, 0);
-        if (drt.mm_ptr == MAP_FAILED) {
-                warn("mmap(%s)", root2id_file);
-                return false;
-        }
+        if (drt.mm_ptr == MAP_FAILED)
+                err(EXIT_FAILURE, "mmap(%zu, %s)", drt.sb.st_size,root2id_file);
         drt.entries = (char **)calloc(est * 2, sizeof(char *));
-        if (!drt.entries) {
-                warn("calloc(%zu * 2, %zu)", est, sizeof(char *));
-                return false;
-        }
+        if (!drt.entries)
+                err(EXIT_FAILURE, "calloc(%zu * 2, %zu)", est, sizeof(char *));
         size_t tot = split2argv(drt.entries, (char *)drt.mm_ptr,
                                 drt.sb.st_size, est * 2);
         if (tot <= 0) return false; // split2argv already warned on error
-        if (!hcreate(est)) {
-                warn("hcreate(%zu)", est);
-                return false;
-        }
+        if (!hcreate(est))
+                err(EXIT_FAILURE, "hcreate(%zu)", est);
         for (size_t i = 0; i < tot; ) {
                 ENTRY e;
-                e.key = hsearch_enter_key(drt.entries[i++]);
-                if (!e.key) return false;
+                e.key = hsearch_enter_key(drt.entries[i++]); // dies on ENOMEM
                 e.data = drt.entries[i++];
-                if (!hsearch(e, ENTER)) {
-                        warn("hsearch(%s => %s, ENTER)", e.key,
-                                (const char *)e.data);
-                        return false;
-                }
+                if (!hsearch(e, ENTER))
+                        err(EXIT_FAILURE, "hsearch(%s => %s, ENTER)", e.key,
+                                        (const char *)e.data);
         }
         req->asc = true;
         req->sort_col = -1;
@@ -581,8 +547,8 @@ static bool cmd_dump_roots(struct req *req)
         // @UNIQ_FOLD in CodeSearchIdx.pm can handle duplicate lines fine
         // in case we need to retry on DB reopens
         for (Xapian::MSetIterator i = mset.begin(); i != mset.end(); i++) {
-                if (!drt.wbuf.fp && !fbuf_init(&drt.wbuf))
-                        return false;
+                if (!drt.wbuf.fp)
+                        fbuf_init(&drt.wbuf);
                 for (int t = 10; t > 0; --t)
                         switch (dump_roots_iter(req, &drt, &i)) {
                         case ITER_OK: t = 0; break; // leave inner loop
@@ -672,6 +638,8 @@ again:
 
         // success! no signals for the rest of the request/response cycle
         CHECK(int, 0, sigprocmask(SIG_SETMASK, &fullset, NULL));
+        if (r > 0 && msg.msg_flags)
+                ABORT("unexpected msg_flags");
 
         *len = r;
         if (cmsg.hdr.cmsg_level == SOL_SOCKET &&
@@ -806,7 +774,7 @@ static void dispatch(struct req *req)
                         break;
                 }
         }
-        if (!req->fn) goto cmd_err;
+        if (!req->fn) ABORT("not handled: `%s'", req->argv[0]);
 
         kfp = open_memstream(&fbuf.ptr, &size);
         // write padding, first
@@ -825,53 +793,48 @@ static void dispatch(struct req *req)
                 case 'd': fwrite(optarg, strlen(optarg) + 1, 1, kfp); break;
                 case 'k':
                         req->sort_col = strtol(optarg, &end, 10);
-                        if (*end) goto cmd_err;
+                        if (*end) ABORT("-k %s", optarg);
                         switch (req->sort_col) {
-                        case LONG_MAX: case LONG_MIN: goto cmd_err;
+                        case LONG_MAX: case LONG_MIN: ABORT("-k %s", optarg);
                         }
                         break;
                 case 'm':
                         req->max = strtoull(optarg, &end, 10);
-                        if (*end) goto cmd_err;
-                        if (req->max == ULLONG_MAX) goto cmd_err;
+                        if (*end || req->max == ULLONG_MAX)
+                                ABORT("-m %s", optarg);
                         break;
                 case 'o':
                         req->off = strtoull(optarg, &end, 10);
-                        if (*end) goto cmd_err;
-                        if (req->off == ULLONG_MAX) goto cmd_err;
+                        if (*end || req->off == ULLONG_MAX)
+                                ABORT("-o %s", optarg);
                         break;
                 case 'r': req->relevance = true; break;
                 case 't': req->collapse_threads = true; break;
                 case 'A':
                         req->pfxv[req->pfxc++] = optarg;
-                        if (MY_ARG_MAX == req->pfxc) goto cmd_err;
+                        if (MY_ARG_MAX == req->pfxc)
+                                ABORT("too many -A");
                         break;
                 case 'O': req->Oeidx_key = optarg - 1; break; // pad "O" prefix
                 case 'T':
                         req->timeout_sec = strtoul(optarg, &end, 10);
-                        if (*end) goto cmd_err;
-                        if (req->timeout_sec == ULONG_MAX) goto cmd_err;
+                        if (*end || req->timeout_sec == ULONG_MAX)
+                                ABORT("-T %s", optarg);
                         break;
-                default: goto cmd_err;
+                default: ABORT("bad switch `-%c'", c);
                 }
         }
-        if (ferror(kfp) | fclose(kfp)) {
-                perror("ferror|fclose");
-                goto cmd_err;
-        }
+        if (ferror(kfp) | fclose(kfp))
+                err(EXIT_FAILURE, "ferror|fclose"); // likely ENOMEM
         fbuf.srch->db = NULL;
         fbuf.srch->qp = NULL;
         fbuf.srch->paths_len = size - offsetof(struct srch, paths);
         if (fbuf.srch->paths_len <= 0) {
                 free_srch(fbuf.srch);
-                warnx("no -d args");
-                goto cmd_err;
+                ABORT("no -d args");
         }
         s = (struct srch **)tsearch(fbuf.srch, &srch_tree, srch_cmp);
-        if (!s) {
-                perror("tsearch");
-                goto cmd_err;
-        }
+        if (!s) err(EXIT_FAILURE, "tsearch"); // likely ENOMEM
         req->srch = *s;
         if (req->srch != fbuf.srch) { // reuse existing
                 free_srch(fbuf.srch);
@@ -881,11 +844,11 @@ static void dispatch(struct req *req)
                 void *del = tdelete(fbuf.srch, &srch_tree, srch_cmp);
                 assert(del);
                 free_srch(fbuf.srch);
-                goto cmd_err;
+                goto cmd_err; // srch_init already warned
         }
         try {
                 if (!req->fn(req))
-                        goto cmd_err;
+                        warnx("`%s' failed", req->argv[0]);
         } catch (const Xapian::Error & e) {
                 warnx("Xapian::Error: %s", e.get_description().c_str());
         } catch (...) {
@@ -924,7 +887,7 @@ static void stderr_restore(FILE *tmp_err)
         return;
 #endif
         if (ferror(stderr) | fflush(stderr))
-                perror("ferror|fflush stderr");
+                err(EXIT_FAILURE, "ferror|fflush stderr");
         while (dup2(orig_err_fd, STDERR_FILENO) < 0) {
                 if (errno != EINTR)
                         err(EXIT_FAILURE, "dup2(%d => 2)", orig_err_fd);
@@ -953,8 +916,7 @@ static void recv_loop(void) // worker process loop
                 if (req.fp[1])
                         stderr_set(req.fp[1]);
                 req.argc = (int)SPLIT2ARGV(req.argv, rbuf, len);
-                if (req.argc > 0)
-                        dispatch(&req);
+                dispatch(&req);
                 if (ferror(req.fp[0]) | fclose(req.fp[0]))
                         perror("ferror|fclose fp[0]");
                 if (req.fp[1]) {
@@ -1066,7 +1028,7 @@ static void reaped_worker(pid_t pid, int st)
         if (WIFEXITED(st) && WEXITSTATUS(st) == EX_NOINPUT)
                 alive = false;
         else if (st)
-                warnx("worker[%lu] died $?=%d", nr, st);
+                warnx("worker[%lu] died $?=%d alive=%d", nr, st, (int)alive);
         if (alive)
                 start_workers();
 }