s6-fdholderd.c (23572B)
1 /* ISC license. */ 2 3 #include <sys/uio.h> 4 #include <sys/stat.h> 5 #include <sys/resource.h> 6 #include <string.h> 7 #include <stdint.h> 8 #include <unistd.h> 9 #include <fcntl.h> 10 #include <errno.h> 11 #include <signal.h> 12 #include <regex.h> 13 14 #include <skalibs/posixplz.h> 15 #include <skalibs/types.h> 16 #include <skalibs/allreadwrite.h> 17 #include <skalibs/sgetopt.h> 18 #include <skalibs/env.h> 19 #include <skalibs/bytestr.h> 20 #include <skalibs/error.h> 21 #include <skalibs/strerr.h> 22 #include <skalibs/tai.h> 23 #include <skalibs/djbunix.h> 24 #include <skalibs/sig.h> 25 #include <skalibs/iopause.h> 26 #include <skalibs/selfpipe.h> 27 #include <skalibs/cdb.h> 28 #include <skalibs/socket.h> 29 #include <skalibs/genset.h> 30 #include <skalibs/avltreen.h> 31 #include <skalibs/unixmessage.h> 32 #include <skalibs/unixconnection.h> 33 34 #include <s6/accessrules.h> 35 #include <s6/fdholder.h> 36 37 #include <skalibs/posixishard.h> 38 39 #define USAGE "s6-fdholderd [ -v verbosity ] [ -1 ] [ -c maxconn ] [ -n maxfds ] [ -t timeout ] [ -T lameducktimeout ] [ -i rulesdir | -x rulesfile ]" 40 #define dieusage() strerr_dieusage(100, USAGE) ; 41 42 static unsigned int verbosity = 1 ; 43 static int cont = 1 ; 44 static tain answertto = TAIN_INFINITE_RELATIVE ; 45 static tain lameduckdeadline = TAIN_INFINITE_RELATIVE ; 46 static tain const nano1 = { .sec = TAI_ZERO, .nano = 1 } ; 47 48 static unsigned int rulestype = 0 ; 49 static char const *rules = 0 ; 50 static cdb cdbmap = CDB_ZERO ; 51 52 static void handle_signals (void) 53 { 54 for (;;) switch (selfpipe_read()) 55 { 56 case -1 : strerr_diefu1sys(111, "selfpipe_read()") ; 57 case 0 : return ; 58 case SIGTERM : 59 { 60 if (cont) 61 { 62 cont = 0 ; 63 tain_add_g(&lameduckdeadline, &lameduckdeadline) ; 64 } 65 break ; 66 } 67 case SIGHUP : 68 { 69 cdb c = CDB_ZERO ; 70 if (rulestype != 2) break ; 71 if (!cdb_init(&c, rules)) break ; 72 cdb_free(&cdbmap) ; 73 cdbmap = c ; 74 } 75 break ; 76 default : break ; 77 } 78 } 79 80 81 /* fd store */ 82 83 static genset *fdstore ; 84 #define FD(i) genset_p(s6_fdholder_fd_t, fdstore, (i)) 85 static unsigned int maxfds = 1000 ; 86 #define numfds genset_n(fdstore) 87 static avltreen *fds_by_id ; 88 static avltreen *fds_by_deadline ; 89 90 static void *fds_id_dtok (uint32_t d, void *x) 91 { 92 (void)x ; 93 return FD(d)->id ; 94 } 95 96 static int fds_id_cmp (void const *a, void const *b, void *x) 97 { 98 (void)x ; 99 return strcmp((char const *)a, (char const *)b) ; 100 } 101 102 static void *fds_deadline_dtok (uint32_t d, void *x) 103 { 104 (void)x ; 105 return &FD(d)->limit ; 106 } 107 108 static int fds_deadline_cmp (void const *a, void const *b, void *x) 109 { 110 tain const *aa = (tain const *)a ; 111 tain const *bb = (tain const *)b ; 112 (void)x ; 113 return tain_less(aa, bb) ? -1 : tain_less(bb, aa) ; 114 } 115 116 static void fds_delete (uint32_t pp) 117 { 118 avltreen_delete(fds_by_id, fds_id_dtok(pp, 0)) ; 119 avltreen_delete(fds_by_deadline, fds_deadline_dtok(pp, 0)) ; 120 genset_delete(fdstore, pp) ; 121 } 122 123 static void fds_close_and_delete (uint32_t pp) 124 { 125 fd_close(FD(pp)->fd) ; 126 fds_delete(pp) ; 127 } 128 129 130 /* client connection */ 131 132 typedef struct client_s client_t, *client_t_ref ; 133 struct client_s 134 { 135 uint32_t next ; 136 uint32_t xindex ; 137 tain deadline ; 138 regex_t rre ; 139 regex_t wre ; 140 unsigned int dumping ; 141 unsigned int flags ; 142 unixconnection connection ; 143 } ; 144 145 static genset *clients ; 146 static uint32_t sentinel ; 147 #define CLIENT(i) genset_p(client_t, clients, (i)) 148 #define numconn (genset_n(clients) - 1) 149 150 static inline void client_free (client_t *c) 151 { 152 fd_close(unixmessage_sender_fd(&c->connection.out)) ; 153 unixconnection_free(&c->connection) ; 154 regfree(&c->rre) ; 155 regfree(&c->wre) ; 156 } 157 158 static inline void client_delete (uint32_t cc, uint32_t prev) 159 { 160 client_t *c = CLIENT(cc) ; 161 CLIENT(prev)->next = c->next ; 162 client_free(c) ; 163 genset_delete(clients, cc) ; 164 } 165 166 static void removeclient (uint32_t *i, uint32_t j) 167 { 168 client_delete(*i, j) ; 169 *i = j ; 170 } 171 172 static void client_setdeadline (client_t *c) 173 { 174 tain blah ; 175 tain_half(&blah, &tain_infinite_relative) ; 176 tain_add_g(&blah, &blah) ; 177 if (tain_less(&blah, &c->deadline)) 178 tain_add_g(&c->deadline, &answertto) ; 179 } 180 181 static inline int client_prepare_iopause (uint32_t i, tain *deadline, iopause_fd *x, uint32_t *j) 182 { 183 client_t *c = CLIENT(i) ; 184 if (tain_less(&c->deadline, deadline)) *deadline = c->deadline ; 185 if (!unixmessage_sender_isempty(&c->connection.out) || !unixmessage_receiver_isempty(&c->connection.in) || (cont && !unixmessage_receiver_isfull(&c->connection.in))) 186 { 187 x[*j].fd = unixmessage_sender_fd(&c->connection.out) ; 188 x[*j].events = (!unixmessage_receiver_isempty(&c->connection.in) || (cont && !unixmessage_receiver_isfull(&c->connection.in)) ? IOPAUSE_READ : 0) | (!unixmessage_sender_isempty(&c->connection.out) ? IOPAUSE_WRITE : 0) ; 189 c->xindex = (*j)++ ; 190 } 191 else c->xindex = 0 ; 192 return !!c->xindex ; 193 } 194 195 static inline void client_add (uint32_t *cc, int fd, regex_t const *rre, regex_t const *wre, unsigned int flags) 196 { 197 uint32_t i ; 198 client_t *c ; 199 i = genset_new(clients) ; 200 c = CLIENT(i) ; 201 tain_add_g(&c->deadline, &answertto) ; 202 c->rre = *rre ; 203 c->wre = *wre ; 204 c->dumping = 0 ; 205 c->flags = flags ; 206 unixconnection_init(&c->connection, fd, fd) ; 207 c->next = CLIENT(sentinel)->next ; 208 CLIENT(sentinel)->next = i ; 209 *cc = i ; 210 } 211 212 static inline int client_flush (uint32_t i, iopause_fd const *x) 213 { 214 client_t *c = CLIENT(i) ; 215 if (c->xindex && (x[c->xindex].revents & IOPAUSE_WRITE)) 216 { 217 if (unixconnection_flush(&c->connection)) 218 tain_add_g(&c->deadline, &tain_infinite_relative) ; 219 else if (!error_isagain(errno)) return 0 ; 220 } 221 return 1 ; 222 } 223 224 static int answer (client_t *c, char e) 225 { 226 unixmessage m = { .s = &e, .len = 1, .fds = 0, .nfds = 0 } ; 227 if (!unixmessage_put(&c->connection.out, &m)) return 0 ; 228 client_setdeadline(c) ; 229 return 1 ; 230 } 231 232 static int do_store (uint32_t cc, unixmessage const *m) 233 { 234 uint32_t pp, idlen ; 235 client_t *c = CLIENT(cc) ; 236 s6_fdholder_fd_t *p ; 237 if (c->dumping || m->len < TAIN_PACK + 3 || m->nfds != 1) return (errno = EPROTO, 0) ; 238 idlen = (unsigned char)m->s[TAIN_PACK] ; 239 if (idlen > S6_FDHOLDER_ID_SIZE || idlen + 2 + TAIN_PACK != m->len || m->s[1 + TAIN_PACK + idlen]) return (errno = EPROTO, 0) ; 240 if (regexec(&c->wre, m->s + TAIN_PACK + 1, 0, 0, 0)) 241 { 242 unixmessage_drop(m) ; 243 return answer(c, EPERM) ; 244 } 245 if (numfds >= maxfds) 246 { 247 unixmessage_drop(m) ; 248 return answer(c, ENFILE) ; 249 } 250 if (avltreen_search(fds_by_id, m->s + TAIN_PACK + 1, &pp)) 251 { 252 unixmessage_drop(m) ; 253 return answer(c, EBUSY) ; 254 } 255 if (!answer(c, 0)) return 0 ; 256 pp = genset_new(fdstore) ; p = FD(pp) ; 257 tain_unpack(m->s, &p->limit) ; p->fd = m->fds[0] ; 258 memcpy(p->id, m->s + TAIN_PACK + 1, idlen) ; 259 memset(p->id + idlen, 0, S6_FDHOLDER_ID_SIZE + 1 - idlen) ; 260 for (;;) 261 { 262 uint32_t dummy ; 263 if (!avltreen_search(fds_by_deadline, &p->limit, &dummy)) break ; 264 tain_add(&p->limit, &p->limit, &nano1) ; 265 } 266 avltreen_insert(fds_by_id, pp) ; 267 avltreen_insert(fds_by_deadline, pp) ; 268 return 1 ; 269 } 270 271 static int do_delete (uint32_t cc, unixmessage const *m) 272 { 273 uint32_t pp, idlen ; 274 client_t *c = CLIENT(cc) ; 275 if (c->dumping || m->len < 3 || m->nfds) return (errno = EPROTO, 0) ; 276 idlen = (unsigned char)m->s[0] ; 277 if (idlen > S6_FDHOLDER_ID_SIZE || idlen + 2 != m->len || m->s[idlen + 1]) return (errno = EPROTO, 0) ; 278 if (regexec(&c->wre, m->s + 1, 0, 0, 0)) return answer(c, EPERM) ; 279 if (!avltreen_search(fds_by_id, m->s + 1, &pp)) return answer(c, ENOENT) ; 280 if (!answer(c, 0)) return 0 ; 281 fds_close_and_delete(pp) ; 282 return 1 ; 283 } 284 285 static int do_retrieve (uint32_t cc, unixmessage const *m) 286 { 287 int fd ; 288 unixmessage ans = { .s = "", .len = 1, .fds = &fd, .nfds = 1 } ; 289 uint32_t pp, idlen ; 290 client_t *c = CLIENT(cc) ; 291 if (c->dumping || m->len < 4 || m->nfds) return (errno = EPROTO, 0) ; 292 idlen = (unsigned char)m->s[1] ; 293 if (idlen > S6_FDHOLDER_ID_SIZE || idlen + 3 != m->len || m->s[idlen + 2]) return (errno = EPROTO, 0) ; 294 if (regexec(&c->rre, m->s + 2, 0, 0, 0)) return answer(c, EPERM) ; 295 if (m->s[0] && regexec(&c->wre, m->s + 2, 0, 0, 0)) return answer(c, EPERM) ; 296 if (!avltreen_search(fds_by_id, m->s + 2, &pp)) return answer(c, ENOENT) ; 297 fd = FD(pp)->fd ; 298 if (!unixmessage_put_and_close(&c->connection.out, &ans, m->s[0] ? unixmessage_bits_closeall : unixmessage_bits_closenone)) return 0 ; 299 if (m->s[0]) fds_delete(pp) ; 300 return 1 ; 301 } 302 303 static int fill_siovec_with_ids_iter (void *thing, void *data) 304 { 305 struct iovec *v = (*(struct iovec **)data)++ ; 306 s6_fdholder_fd_t *p = thing ; 307 v->iov_base = p->id ; 308 v->iov_len = strlen(p->id) + 1 ; 309 return 1 ; 310 } 311 312 static int do_list (uint32_t cc, unixmessage const *m) 313 { 314 client_t *c = CLIENT(cc) ; 315 struct iovec v[1+numfds] ; 316 unixmessagev ans = { .v = v, .vlen = 1+numfds, .fds = 0, .nfds = 0 } ; 317 struct iovec *vp = v + 1 ; 318 char pack[5] = "" ; 319 if (c->dumping || m->len || m->nfds) return (errno = EPROTO, 0) ; 320 if (!(c->flags & 4)) return answer(c, EPERM) ; 321 uint32_pack_big(pack + 1, (uint32_t)numfds) ; 322 v[0].iov_base = pack ; v[0].iov_len = 5 ; 323 genset_iter(fdstore, &fill_siovec_with_ids_iter, &vp) ; 324 if (!unixmessage_putv(&c->connection.out, &ans)) return answer(c, errno) ; 325 client_setdeadline(c) ; 326 return 1 ; 327 } 328 329 typedef struct getdumpiter_s getdumpiter_t, *getdumpiter_t_ref ; 330 struct getdumpiter_s 331 { 332 struct iovec *v ; 333 int *fd ; 334 char *limit ; 335 } ; 336 337 static int getdump_iter (void *thing, void *stuff) 338 { 339 s6_fdholder_fd_t *p = thing ; 340 getdumpiter_t *blah = stuff ; 341 unsigned char len = strlen(p->id) ; 342 tain_pack(blah->limit, &p->limit) ; 343 blah->limit[TAIN_PACK] = len ; 344 blah->v->iov_base = p->id ; 345 blah->v->iov_len = len + 1 ; 346 *blah->fd++ = p->fd ; 347 blah->v += 2 ; 348 blah->limit += TAIN_PACK + 1 ; 349 return 1 ; 350 } 351 352 static int do_getdump (uint32_t cc, unixmessage const *m) 353 { 354 uint32_t n = numfds ? 1 + ((numfds-1) / UNIXMESSAGE_MAXFDS) : 0 ; 355 client_t *c = CLIENT(cc) ; 356 if (c->dumping || m->len || m->nfds) return (errno = EPROTO, 0) ; 357 if (!(c->flags & 1)) return answer(c, EPERM) ; 358 { 359 char pack[9] = "" ; 360 unixmessage ans = { .s = pack, .len = 9, .fds = 0, .nfds = 0 } ; 361 uint32_pack_big(pack+1, n) ; 362 uint32_pack_big(pack+5, numfds) ; 363 if (!unixmessage_put(&c->connection.out, &ans)) return answer(c, errno) ; 364 } 365 if (n) 366 { 367 uint32_t i = 0 ; 368 unixmessagev ans[n] ; 369 struct iovec v[numfds << 1] ; 370 int fds[numfds] ; 371 char limits[(TAIN_PACK+1) * numfds] ; 372 for (; i < numfds ; i++) 373 { 374 v[i<<1].iov_base = limits + i * (TAIN_PACK+1) ; 375 v[i<<1].iov_len = TAIN_PACK+1 ; 376 } 377 { 378 getdumpiter_t state = { .v = v+1, .fd = fds, .limit = limits } ; 379 genset_iter(fdstore, &getdump_iter, &state) ; 380 } 381 for (i = 0 ; i < n-1 ; i++) 382 { 383 ans[i].v = v + (i<<1) * UNIXMESSAGE_MAXFDS ; 384 ans[i].vlen = UNIXMESSAGE_MAXFDS << 1 ; 385 ans[i].fds = fds + i * UNIXMESSAGE_MAXFDS ; 386 ans[i].nfds = UNIXMESSAGE_MAXFDS ; 387 } 388 ans[n-1].v = v + ((n-1)<<1) * UNIXMESSAGE_MAXFDS ; 389 ans[n-1].vlen = (1 + (numfds-1) % UNIXMESSAGE_MAXFDS) << 1 ; 390 ans[n-1].fds = fds + (n-1) * UNIXMESSAGE_MAXFDS ; 391 ans[n-1].nfds = 1 + (numfds - 1) % UNIXMESSAGE_MAXFDS ; 392 for (i = 0 ; i < n ; i++) 393 { 394 if (!unixmessage_putv(&c->connection.out, ans + i)) 395 { 396 int e = errno ; 397 ++i ; 398 while (i--) unixmessage_unput(&c->connection.out) ; 399 return answer(c, e) ; 400 } 401 } 402 } 403 client_setdeadline(c) ; 404 return 1 ; 405 } 406 407 static int do_setdump (uint32_t cc, unixmessage const *m) 408 { 409 char pack[5] = "" ; 410 uint32_t n ; 411 unixmessage ans = { .s = pack, .len = 5, .fds = 0, .nfds = 0 } ; 412 client_t *c = CLIENT(cc) ; 413 if (c->dumping || m->len != 4 || m->nfds) return (errno = EPROTO, 0) ; 414 if (!(c->flags & 2)) return answer(c, EPERM) ; 415 uint32_unpack_big(m->s, &n) ; 416 if (n > maxfds || n + numfds > maxfds) return answer(c, ENFILE) ; 417 c->dumping = n ; 418 n = n ? 1 + (n-1) / UNIXMESSAGE_MAXFDS : 0 ; 419 uint32_pack_big(pack+1, n) ; 420 if (!unixmessage_put(&c->connection.out, &ans)) return answer(c, errno) ; 421 return 1 ; 422 } 423 424 static int do_setdump_data (uint32_t cc, unixmessage const *m) 425 { 426 client_t *c = CLIENT(cc) ; 427 if (!m->nfds || m->nfds > c->dumping || m->len < m->nfds * (TAIN_PACK + 3)) 428 return (errno = EPROTO, 0) ; 429 if (!(c->flags & 2)) 430 { 431 unixmessage_drop(m) ; 432 return answer(c, EPERM) ; 433 } 434 if (numfds + m->nfds > maxfds) 435 { 436 unixmessage_drop(m) ; 437 return answer(c, ENFILE) ; 438 } 439 { 440 char const *s = m->s ; 441 size_t len = m->len ; 442 uint32_t i = 0 ; 443 uint32_t indices[m->nfds] ; 444 for (; i < m->nfds ; i++) 445 { 446 s6_fdholder_fd_t *p ; 447 uint32_t oldid, idlen ; 448 if (len < TAIN_PACK + 3) break ; 449 idlen = (unsigned char)s[TAIN_PACK] ; 450 if (idlen > S6_FDHOLDER_ID_SIZE || len < TAIN_PACK + 2 + idlen || s[TAIN_PACK + 1 + idlen]) break ; 451 indices[i] = genset_new(fdstore) ; 452 p = FD(indices[i]) ; 453 tain_unpack(s, &p->limit) ; 454 memcpy(p->id, s + TAIN_PACK + 1, idlen+1) ; 455 p->fd = m->fds[i] ; 456 if (avltreen_search(fds_by_id, p->id, &oldid)) fds_close_and_delete(oldid) ; 457 avltreen_insert(fds_by_id, indices[i]) ; 458 avltreen_insert(fds_by_deadline, indices[i]) ; 459 s += TAIN_PACK + 2 + idlen ; len -= TAIN_PACK + 2 + idlen ; 460 } 461 if (i < m->nfds || len) 462 { 463 while (i--) fds_delete(indices[i]) ; 464 return (errno = EPROTO, 0) ; 465 } 466 } 467 c->dumping -= m->nfds ; 468 return answer(c, c->dumping ? EAGAIN : 0) ; 469 } 470 471 static int do_error (uint32_t cc, unixmessage const *m) 472 { 473 (void)cc ; 474 (void)m ; 475 return (errno = EPROTO, 0) ; 476 } 477 478 typedef int parse_func (uint32_t, unixmessage const *) ; 479 typedef parse_func *parse_func_ref ; 480 481 static inline int parse_protocol (unixmessage const *m, void *p) 482 { 483 static parse_func_ref const f[8] = 484 { 485 &do_store, 486 &do_delete, 487 &do_retrieve, 488 &do_list, 489 &do_getdump, 490 &do_setdump, 491 &do_setdump_data, 492 &do_error 493 } ; 494 unixmessage mcopy = { .s = m->s + 1, .len = m->len - 1, .fds = m->fds, .nfds = m->nfds } ; 495 if (!m->len) 496 { 497 unixmessage_drop(m) ; 498 return (errno = EPROTO, 0) ; 499 } 500 if (!(*f[byte_chr("SDRL?!.", 7, m->s[0])])(*(uint32_t *)p, &mcopy)) 501 { 502 unixmessage_drop(m) ; 503 return 0 ; 504 } 505 return 1 ; 506 } 507 508 static inline int client_read (uint32_t cc, iopause_fd const *x) 509 { 510 client_t *c = CLIENT(cc) ; 511 return !unixmessage_receiver_isempty(&c->connection.in) || (c->xindex && (x[c->xindex].revents & IOPAUSE_READ)) ? 512 unixmessage_handle(&c->connection.in, &parse_protocol, &cc) > 0 : 1 ; 513 } 514 515 516 /* Environment on new connections */ 517 518 static int makere (regex_t *re, char const *s, char const *var) 519 { 520 size_t varlen = strlen(var) ; 521 if (str_start(s, var) && (s[varlen] == '=')) 522 { 523 int r = skalibs_regcomp(re, s + varlen + 1, REG_EXTENDED | REG_NOSUB) ; 524 if (r) 525 { 526 if (verbosity) 527 { 528 char buf[256] ; 529 regerror(r, re, buf, 256) ; 530 strerr_warnw6x("invalid ", var, " value: ", s + varlen + 1, ": ", buf) ; 531 } 532 return -1 ; 533 } 534 else return 1 ; 535 } 536 return 0 ; 537 } 538 539 static void defaultre (regex_t *re) 540 { 541 int r = skalibs_regcomp(re, ".^", REG_EXTENDED | REG_NOSUB) ; 542 if (r) 543 { 544 char buf[256] ; 545 regerror(r, re, buf, 256) ; 546 strerr_diefu2x(100, "compile .^ into a regular expression: ", buf) ; 547 } 548 } 549 550 static inline int parse_env (char const *const *envp, regex_t *rre, regex_t *wre, unsigned int *flags, unsigned int *donep) 551 { 552 unsigned int done = 0 ; 553 unsigned int fl = 0 ; 554 for (; *envp ; envp++) 555 { 556 if (str_start(*envp, "S6_FDHOLDER_GETDUMP=")) fl |= 1 ; 557 if (str_start(*envp, "S6_FDHOLDER_SETDUMP=")) fl |= 2 ; 558 if (str_start(*envp, "S6_FDHOLDER_LIST=")) fl |= 4 ; 559 if (!(done & 1)) 560 { 561 int r = makere(rre, *envp, "S6_FDHOLDER_RETRIEVE_REGEX") ; 562 if (r < 0) 563 { 564 if (done & 2) regfree(wre) ; 565 return 0 ; 566 } 567 else if (r) done |= 1 ; 568 } 569 if (!(done & 2)) 570 { 571 int r = makere(wre, *envp, "S6_FDHOLDER_STORE_REGEX") ; 572 if (r < 0) 573 { 574 if (done & 1) regfree(rre) ; 575 return 0 ; 576 } 577 else if (r) done |= 2 ; 578 } 579 } 580 *flags = fl ; 581 *donep = done ; 582 return 1 ; 583 } 584 585 static inline int new_connection (int fd, regex_t *rre, regex_t *wre, unsigned int *flags) 586 { 587 s6_accessrules_params_t params = S6_ACCESSRULES_PARAMS_ZERO ; 588 s6_accessrules_result_t result = S6_ACCESSRULES_ERROR ; 589 uid_t uid ; 590 gid_t gid ; 591 unsigned int done = 0 ; 592 593 if (getpeereid(fd, &uid, &gid) < 0) 594 { 595 if (verbosity) strerr_warnwu1sys("getpeereid") ; 596 return 0 ; 597 } 598 599 switch (rulestype) 600 { 601 case 1 : 602 result = s6_accessrules_uidgid_fs(uid, gid, rules, ¶ms) ; break ; 603 case 2 : 604 result = s6_accessrules_uidgid_cdb(uid, gid, &cdbmap, ¶ms) ; break ; 605 default : break ; 606 } 607 if (result != S6_ACCESSRULES_ALLOW) 608 { 609 if (verbosity && (result == S6_ACCESSRULES_ERROR)) 610 strerr_warnw1sys("error while checking rules") ; 611 return 0 ; 612 } 613 if (params.exec.len && verbosity) 614 { 615 char fmtuid[UID_FMT] ; 616 char fmtgid[GID_FMT] ; 617 fmtuid[uid_fmt(fmtuid, uid)] = 0 ; 618 fmtgid[gid_fmt(fmtgid, gid)] = 0 ; 619 strerr_warnw4x("unused exec string in rules for uid ", fmtuid, " gid ", fmtgid) ; 620 } 621 if (params.env.s) 622 { 623 size_t n = byte_count(params.env.s, params.env.len, '\0') ; 624 char const *envp[n+1] ; 625 if (!env_make(envp, n, params.env.s, params.env.len)) 626 { 627 if (verbosity) strerr_warnwu1sys("env_make") ; 628 s6_accessrules_params_free(¶ms) ; 629 return 0 ; 630 } 631 envp[n] = 0 ; 632 if (!parse_env(envp, rre, wre, flags, &done)) 633 { 634 if (verbosity) strerr_warnwu1sys("parse_env") ; 635 s6_accessrules_params_free(¶ms) ; 636 return 0 ; 637 } 638 } 639 s6_accessrules_params_free(¶ms) ; 640 if (!(done & 1)) defaultre(rre) ; 641 if (!(done & 2)) defaultre(wre) ; 642 return 1 ; 643 } 644 645 646 int main (int argc, char const *const *argv, char const *const *envp) 647 { 648 int flag1 = 0 ; 649 uint32_t maxconn = 16 ; 650 PROG = "s6-fdholderd" ; 651 652 { 653 subgetopt l = SUBGETOPT_ZERO ; 654 unsigned int t = 0, T = 0 ; 655 for (;;) 656 { 657 int opt = subgetopt_r(argc, argv, "v:1c:n:i:x:t:T:", &l) ; 658 if (opt == -1) break ; 659 switch (opt) 660 { 661 case 'v' : if (!uint0_scan(l.arg, &verbosity)) dieusage() ; break ; 662 case '1' : flag1 = 1 ; break ; 663 case 'i' : rules = l.arg ; rulestype = 1 ; break ; 664 case 'x' : rules = l.arg ; rulestype = 2 ; break ; 665 case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ; 666 case 'T' : if (!uint0_scan(l.arg, &T)) dieusage() ; break ; 667 case 'c' : if (!uint0_scan(l.arg, &maxconn)) dieusage() ; break ; 668 case 'n' : if (!uint0_scan(l.arg, &maxfds)) dieusage() ; break ; 669 default : dieusage() ; 670 } 671 } 672 argc -= l.ind ; argv += l.ind ; 673 if (t) tain_from_millisecs(&answertto, t) ; 674 if (T) tain_from_millisecs(&lameduckdeadline, T) ; 675 } 676 if (!rulestype) strerr_dief1x(100, "no access rights specified!") ; 677 if (maxconn > S6_FDHOLDER_MAX) maxconn = S6_FDHOLDER_MAX ; 678 if (!maxconn) maxconn = 1 ; 679 { 680 struct rlimit fdlimit ; 681 if (getrlimit(RLIMIT_NOFILE, &fdlimit) < 0) 682 strerr_diefu1sys(111, "getrlimit") ; 683 if (fdlimit.rlim_cur != RLIM_INFINITY) 684 { 685 if (fdlimit.rlim_cur < 7) 686 strerr_dief1x(111, "open file limit too low") ; 687 if (maxfds > fdlimit.rlim_cur) maxfds = fdlimit.rlim_cur - 6 ; 688 } 689 } 690 if (!maxfds) maxfds = 1 ; 691 { 692 struct stat st ; 693 if (fstat(0, &st) < 0) strerr_diefu1sys(111, "fstat stdin") ; 694 if (!S_ISSOCK(st.st_mode)) strerr_dief1x(100, "stdin is not a socket") ; 695 } 696 if (flag1) 697 { 698 if (fcntl(1, F_GETFD) < 0) 699 strerr_dief1sys(100, "called with option -1 but stdout said") ; 700 } 701 else close(1) ; 702 if (selfpipe_init() == -1) strerr_diefu1sys(111, "selfpipe_init") ; 703 if (!sig_ignore(SIGPIPE)) strerr_diefu1sys(111, "ignore SIGPIPE") ; 704 { 705 sigset_t set ; 706 sigemptyset(&set) ; 707 sigaddset(&set, SIGTERM) ; 708 sigaddset(&set, SIGHUP) ; 709 if (!selfpipe_trapset(&set)) strerr_diefu1sys(111, "trap signals") ; 710 } 711 712 if (rulestype == 2) 713 { 714 if (!cdb_init(&cdbmap, rules)) 715 strerr_diefu2sys(111, "cdb_init ", rules) ; 716 } 717 718 { 719 /* Hello, stack. I have a present for you. */ 720 genset clientinfo, fdinfo ; 721 avltreen fdidinfo, fddeadlineinfo ; 722 iopause_fd x[2 + maxconn] ; 723 client_t clientstorage[1+maxconn] ; 724 uint32_t clientfreelist[1+maxconn] ; 725 s6_fdholder_fd_t fdstorage[maxfds] ; 726 uint32_t fdfreelist[maxfds] ; 727 avlnode fdidstorage[maxfds] ; 728 uint32_t fdidfreelist[maxfds] ; 729 avlnode fddeadlinestorage[maxfds] ; 730 uint32_t fddeadlinefreelist[maxfds] ; 731 /* Hope you enjoyed it! Have a nice day! */ 732 733 GENSET_init(&clientinfo, client_t, clientstorage, clientfreelist, 1+maxconn) ; 734 clients = &clientinfo ; 735 sentinel = genset_new(clients) ; 736 clientstorage[sentinel].next = sentinel ; 737 GENSET_init(&fdinfo, s6_fdholder_fd_t, fdstorage, fdfreelist, maxfds) ; 738 fdstore = &fdinfo ; 739 avltreen_init(&fdidinfo, fdidstorage, fdidfreelist, maxfds, &fds_id_dtok, &fds_id_cmp, 0) ; 740 fds_by_id = &fdidinfo ; 741 avltreen_init(&fddeadlineinfo, fddeadlinestorage, fddeadlinefreelist, maxfds, &fds_deadline_dtok, &fds_deadline_cmp, 0) ; 742 fds_by_deadline = &fddeadlineinfo ; 743 744 x[0].fd = selfpipe_fd() ; x[0].events = IOPAUSE_READ ; 745 x[1].fd = 0 ; 746 747 if (flag1) 748 { 749 fd_write(1, "\n", 1) ; 750 fd_close(1) ; 751 } 752 753 /* We are long-lived and have to check absolute fd deadlines, 754 so we purposefully remain in wallclock mode. */ 755 tain_now_g() ; 756 757 for (;;) 758 { 759 tain deadline ; 760 uint32_t j = 2 ; 761 uint32_t i ; 762 int r = 1 ; 763 764 if (cont) tain_add_g(&deadline, &tain_infinite_relative) ; 765 else deadline = lameduckdeadline ; 766 if (avltreen_min(fds_by_deadline, &i) && tain_less(&FD(i)->limit, &deadline)) deadline = FD(i)->limit ; 767 x[1].events = (cont && (numconn < maxconn)) ? IOPAUSE_READ : 0 ; 768 for (i = clientstorage[sentinel].next ; i != sentinel ; i = clientstorage[i].next) 769 if (client_prepare_iopause(i, &deadline, x, &j)) r = 0 ; 770 if (!cont && r) break ; 771 772 r = iopause_g(x, j, &deadline) ; 773 if (r < 0) strerr_diefu1sys(111, "iopause") ; 774 775 if (!r) 776 { 777 if (!cont && !tain_future(&lameduckdeadline)) break ; 778 for (;;) 779 { 780 if (!avltreen_min(fds_by_deadline, &i)) break ; 781 if (tain_future(&FD(i)->limit)) break ; 782 fds_close_and_delete(i) ; 783 } 784 for (j = sentinel, i = clientstorage[sentinel].next ; i != sentinel ; j = i, i = clientstorage[i].next) 785 if (!tain_future(&clientstorage[i].deadline)) removeclient(&i, j) ; 786 continue ; 787 } 788 789 if (x[0].revents & IOPAUSE_READ) handle_signals() ; 790 791 for (j = sentinel, i = clientstorage[sentinel].next ; i != sentinel ; j = i, i = clientstorage[i].next) 792 if (!client_flush(i, x)) removeclient(&i, j) ; 793 794 for (j = sentinel, i = clientstorage[sentinel].next ; i != sentinel ; j = i, i = clientstorage[i].next) 795 if (!client_read(i, x)) removeclient(&i, j) ; 796 797 if (x[1].revents & IOPAUSE_READ) 798 { 799 regex_t rre, wre ; 800 unsigned int flags = 0 ; 801 int dummy ; 802 int fd = ipc_accept_nb(x[1].fd, 0, 0, &dummy) ; 803 if (fd < 0) 804 if (!error_isagain(errno)) strerr_diefu1sys(111, "accept") ; 805 else continue ; 806 else if (!new_connection(fd, &rre, &wre, &flags)) fd_close(fd) ; 807 else client_add(&i, fd, &rre, &wre, flags) ; 808 } 809 } 810 return ((!!numfds) | (!!numconn << 1)) ; 811 } 812 }