static void set_sig_handlers() { int r; struct sigaction sa; sa.sa_handler = SIG_IGN; sa.sa_flags = 0; r = sigemptyset(&sa.sa_mask); if (r == -1) twarn("sigemptyset()"), exit(111); r = sigaction(SIGPIPE, &sa, 0); if (r == -1) twarn("sigaction(SIGPIPE)"), exit(111); sa.sa_handler = enter_drain_mode; r = sigaction(SIGUSR1, &sa, 0); if (r == -1) twarn("sigaction(SIGUSR1)"), exit(111); sa.sa_handler = exit_cleanly; r = sigaction(SIGINT, &sa, 0); if (r == -1) twarn("sigaction(SIGINT)"), exit(111); sa.sa_handler = exit_cleanly; r = sigaction(SIGTERM, &sa, 0); if (r == -1) twarn("sigaction(SIGTERM)"), exit(111); }
/* This is a workaround for a mystifying workaround in libevent's epoll * implementation. The epoll_init() function creates an epoll fd with space to * handle RLIMIT_NOFILE - 1 fds, accompanied by the following puzzling comment: * "Solaris is somewhat retarded - it's important to drop backwards * compatibility when making changes. So, don't dare to put rl.rlim_cur here." * This is presumably to work around a bug in Solaris, but it has the * unfortunate side-effect of causing epoll_ctl() (and, therefore, event_add()) * to fail for a valid fd if we have hit the limit of open fds. That makes it * hard to provide reasonable behavior in that situation. So, let's reduce the * real value of RLIMIT_NOFILE by one, after epoll_init() has run. */ static void nudge_fd_limit() { int r; struct rlimit rl; r = getrlimit(RLIMIT_NOFILE, &rl); if (r != 0) twarn("getrlimit(RLIMIT_NOFILE)"), exit(2); rl.rlim_cur--; r = setrlimit(RLIMIT_NOFILE, &rl); if (r != 0) twarn("setrlimit(RLIMIT_NOFILE)"), exit(2); }
static void su(const char *user) { int r; struct passwd *pwent; errno = 0; pwent = getpwnam(user); if (errno) twarn("getpwnam(\"%s\")", user), exit(32); if (!pwent) twarnx("getpwnam(\"%s\"): no such user", user), exit(33); r = setgid(pwent->pw_gid); if (r == -1) twarn("setgid(%d \"%s\")", pwent->pw_gid, user), exit(34); r = setuid(pwent->pw_uid); if (r == -1) twarn("setuid(%d \"%s\")", pwent->pw_uid, user), exit(34); }
void sockmain() { int i, r, n = 1; int64 e, t = nanoseconds(); struct kevent evs[n]; for (;;) { r = kevent(kq, NULL, 0, evs, n, &ivalts); if (r == -1 && errno != EINTR) { twarn("kevent"); exit(1); } // should tick? e = nanoseconds(); if (e-t > ival) { tick(tickval, 0); t = e; } for (i=0; i<r; i++) { handle(evs[i].udata, evs[i].filter, evs[i].flags); } } }
static void nullfd(int fd, int flags) { int r; close(fd); r = open("/dev/null", flags); if (r != fd) twarn("open(\"/dev/null\")"), exit(1); }
int main(int argc, char **argv) { int r, l; struct event_base *ev_base; struct job binlog_jobs = {}; progname = argv[0]; opts(argc, argv); if (detach && binlog_dir) { if (binlog_dir[0] != '/') { warnx("The -b option requires an absolute path when used with -d."); usage("Path is not absolute", binlog_dir); } } job_init(); prot_init(); /* We want to make sure that only one beanstalkd tries to use the binlog * directory at a time. So acquire a lock now and never release it. */ if (binlog_dir) { r = binlog_lock(); if (!r) twarnx("failed to lock binlog dir %s", binlog_dir), exit(10); } r = make_server_socket(host_addr, port); if (r == -1) twarnx("make_server_socket()"), exit(111); l = r; if (user) su(user); ev_base = event_init(); set_sig_handlers(); nudge_fd_limit(); r = listen(l, 1024); if (r == -1) twarn("listen()"); accept_handler = (evh)h_accept; unbrake(); binlog_jobs.prev = binlog_jobs.next = &binlog_jobs; binlog_init(&binlog_jobs); prot_replay_binlog(&binlog_jobs); if (detach) { daemonize(); event_reinit(ev_base); } event_dispatch(); twarnx("event_dispatch error"); binlog_shutdown(); return 0; }
// Opens f for writing, writes a header, and initializes // f->free and f->resv. // Sets f->iswopen if successful. void filewopen(File *f) { int fd, r; int n; int ver = Walver; fd = open(f->path, O_WRONLY|O_CREAT, 0400); if (fd < 0) { twarn("open %s", f->path); return; } r = falloc(fd, f->w->filesize); if (r) { close(fd); errno = r; twarn("falloc %s", f->path); r = unlink(f->path); if (r) { twarn("unlink %s", f->path); } return; } n = write(fd, &ver, sizeof(int)); if (n < sizeof(int)) { twarn("write %s", f->path); close(fd); return; } f->fd = fd; f->iswopen = 1; fileincref(f); f->free = f->w->filesize - n; f->resv = 0; }
void sockinit(Handle f, void *x, int64 ns) { tick = f; tickval = x; ival = ns; ivalts.tv_sec = ns / 1000000000; ivalts.tv_nsec = ns % 1000000000; kq = kqueue(); if (kq == -1) { twarn("kqueue"); exit(1); } }
static void daemonize() { int r; r = chdir("/"); if (r) return twarn("chdir"); nullfd(0, O_RDONLY); nullfd(1, O_WRONLY); nullfd(2, O_WRONLY); umask(0); dfork(); setsid(); dfork(); }
static int filewrite(File *f, job j, void *buf, int len) { int r; r = write(f->fd, buf, len); if (r != len) { twarn("write"); return 0; } f->w->resv -= r; f->resv -= r; j->walresv -= r; j->walused += r; f->w->alive += r; return 1; }
static int readfull(File *f, void *c, int n, int *err, char *desc) { int r; r = read(f->fd, c, n); if (r == -1) { twarn("read"); warnpos(f, 0, "error reading %s", desc); *err = 1; return 0; } if (r != n) { warnpos(f, -r, "unexpected EOF reading %d bytes (got %d): %s", n, r, desc); *err = 1; return 0; } return r; }
int main() { Vcodex_t *tz, *uz; Vcodex_t *huf, *rle, *mtf; Vcchar_t *mt, *cmp, *tstr; Vcchar_t store[2*sizeof(Mt)]; ssize_t nc, nu, n; Vcmethod_t *Vctable; if(!(Vctable = vcgetmeth("table", 0))) terror("table plugin not found"); if(!(huf = vcopen(0, Vchuffgroup, 0, 0, VC_ENCODE)) ) terror("Can't open Vchuffgroup handle to compress"); if(!(rle = vcopen(0, Vcrle, "0", huf, VC_ENCODE|VC_CLOSECODER)) ) terror("Can't open Vcrle handle to compress"); if(!(mtf = vcopen(0, Vcmtf, 0, rle, VC_ENCODE|VC_CLOSECODER)) ) terror("Can't open Vcrle handle to compress"); if(!(tz = vcopen(0, Vctable, 0, mtf, VC_ENCODE)) ) terror("Vctable: could not open handle to transform"); if((n = vcextract(tz, (Void_t**)(&tstr))) <= 0) terror("Cannot get encoding string"); memcpy(store,tstr,n); if((nc = vcapply(tz, Mt, sizeof(Mt), &cmp)) <= 0 ) terror("Vctable: fail transforming"); twarn("Vctable: rawsz=%d cmpsz=%d\n", sizeof(Mt), nc); if(!(uz = vcrestore(store, n)) ) terror("Vctable: could not recreate handle to decode"); if((nu = vcapply(uz, cmp, nc, &mt)) != sizeof(Mt)) terror("Vctable: fail untransforming"); if(memcmp(mt, Mt, nu) != 0) terror("Vctable: results did not match"); exit(0); }
// Readrec5 is like readrec, but it reads a record in "version 5" // of the log format. static int readrec5(File *f, job l, int *err) { int r, sz = 0; size_t namelen; Jobrec5 jr; job j; tube t; char tubename[MAX_TUBE_NAME_LEN]; r = read(f->fd, &namelen, sizeof(namelen)); if (r == -1) { twarn("read"); warnpos(f, 0, "error"); *err = 1; return 0; } if (r != sizeof(namelen)) { return 0; } sz += r; if (namelen >= MAX_TUBE_NAME_LEN) { warnpos(f, -r, "namelen %zu exceeds maximum of %d", namelen, MAX_TUBE_NAME_LEN - 1); *err = 1; return 0; } if (namelen) { r = readfull(f, tubename, namelen, err, "v5 tube name"); if (!r) { return 0; } sz += r; } tubename[namelen] = '\0'; r = readfull(f, &jr, Jobrec5size, err, "v5 job struct"); if (!r) { return 0; } sz += r; // are we reading trailing zeroes? if (!jr.id) return 0; j = job_find(jr.id); if (!(j || namelen)) { // We read a short record without having seen a // full record for this job, so the full record // was in an eariler file that has been deleted. // Therefore the job itself has either been // deleted or migrated; either way, this record // should be ignored. return 1; } switch (jr.state) { case Reserved: jr.state = Ready; case Ready: case Buried: case Delayed: if (!j) { if (jr.body_size > job_data_size_limit) { warnpos(f, -r, "job %"PRIu64" is too big (%"PRId32" > %zu)", jr.id, jr.body_size, job_data_size_limit); goto Error; } t = tube_find_or_make(tubename); j = make_job_with_id(jr.pri, jr.delay, jr.ttr, jr.body_size, t, jr.id); j->next = j->prev = j; j->r.created_at = jr.created_at; } j->r.id = jr.id; j->r.pri = jr.pri; j->r.delay = jr.delay * 1000; // us => ns j->r.ttr = jr.ttr * 1000; // us => ns j->r.body_size = jr.body_size; j->r.created_at = jr.created_at * 1000; // us => ns j->r.deadline_at = jr.deadline_at * 1000; // us => ns j->r.reserve_ct = jr.reserve_ct; j->r.timeout_ct = jr.timeout_ct; j->r.release_ct = jr.release_ct; j->r.bury_ct = jr.bury_ct; j->r.kick_ct = jr.kick_ct; j->r.state = jr.state; job_insert(l, j); // full record; read the job body if (namelen) { if (jr.body_size != j->r.body_size) { warnpos(f, -r, "job %"PRIu64" size changed", j->r.id); warnpos(f, -r, "was %"PRId32", now %"PRId32, j->r.body_size, jr.body_size); goto Error; } r = readfull(f, j->body, j->r.body_size, err, "v5 job body"); if (!r) { goto Error; } sz += r; // since this is a full record, we can move // the file pointer and decref the old // file, if any filermjob(j->file, j); fileaddjob(f, j); } j->walused += sz; f->w->alive += sz; return 1; case Invalid: if (j) { job_remove(j); filermjob(j->file, j); job_free(j); } return 1; } Error: *err = 1; if (j) { job_remove(j); filermjob(j->file, j); job_free(j); } return 0; }
tmain() { int i, rv; void *status; char *objs[N_THREAD]; pthread_t thread[N_THREAD]; int nthreads = N_THREAD; int iterations = ITERATIONS; int objsize = OBJSIZE; int repetitions = REPETITIONS; #ifdef VMALLOC Vmstat_t vmst; #endif tresource(-1, 0); while(argc > 1) { if(argv[1][0] == '-' && argv[1][1] == 't') nthreads = atoi(argv[1]+2); else if(argv[1][0] == '-' && argv[1][1] == 'i') iterations = atoi(argv[1]+2); else if(argv[1][0] == '-' && argv[1][1] == 'r') repetitions = atoi(argv[1]+2); else if(argv[1][0] == '-' && argv[1][1] == 'z') objsize = atoi(argv[1]+2); argc--; argv++; } if(nthreads <= 0 || nthreads > N_THREAD) terror("nthreads=%d must be in 1..%d", nthreads, N_THREAD); if(repetitions < nthreads) repetitions = 0; tinfo("nthreads=%d iterations=%d objsize=%d repetitions=%d", nthreads, iterations, objsize, repetitions ); for(i = 0; i < nthreads; ++i) if(!(objs[i] = (char*)malloc(objsize)) ) terror("Can't allocate objs[%d]", i); for(i = 0; i < nthreads; ++i) { Worker_t *w = (Worker_t*)malloc(sizeof(Worker_t)); w->object = objs[i]; w->objsize = objsize; w->iterations = iterations; w->repetitions = repetitions/nthreads; if((rv = pthread_create(&thread[i], NULL, worker, (void*)w)) != 0) terror("Failed to create thread %d", i); } for(i = 0; i < nthreads; ++i) if((rv = pthread_join(thread[i], &status)) != 0) terror("Failed waiting for thread %d", i); tresource(0, 0); #ifdef VMALLOC vmstat(0, &vmst); twarn(vmst.mesg); #endif texit(0); }
int make_server_socket(char *host, char *port) { int fd = -1, flags, r; struct linger linger = {0, 0}; struct addrinfo *airoot, *ai, hints; /* See if we got a listen fd from systemd. If so, all socket options etc * are already set, so we check that the fd is a TCP listen socket and * return. */ r = sd_listen_fds(1); if (r < 0) { return twarn("sd_listen_fds"), -1; } if (r > 0) { if (r > 1) { twarnx("inherited more than one listen socket;" " ignoring all but the first"); r = 1; } fd = SD_LISTEN_FDS_START; r = sd_is_socket_inet(fd, 0, SOCK_STREAM, 1, 0); if (r < 0) { errno = -r; twarn("sd_is_socket_inet"); return -1; } if (!r) { twarnx("inherited fd is not a TCP listen socket"); return -1; } return fd; } memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; r = getaddrinfo(host, port, &hints, &airoot); if (r == -1) return twarn("getaddrinfo()"), -1; for(ai = airoot; ai; ai = ai->ai_next) { fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (fd == -1) { twarn("socket()"); continue; } flags = fcntl(fd, F_GETFL, 0); if (flags < 0) { twarn("getting flags"); close(fd); continue; } r = fcntl(fd, F_SETFL, flags | O_NONBLOCK); if (r == -1) { twarn("setting O_NONBLOCK"); close(fd); continue; } flags = 1; setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof flags); setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &flags, sizeof flags); setsockopt(fd, SOL_SOCKET, SO_LINGER, &linger, sizeof linger); setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &flags, sizeof flags); if (verbose) { char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV], *h = host, *p = port; r = getnameinfo(ai->ai_addr, ai->ai_addrlen, hbuf, sizeof hbuf, pbuf, sizeof pbuf, NI_NUMERICHOST|NI_NUMERICSERV); if (!r) { h = hbuf; p = pbuf; } if (ai->ai_family == AF_INET6) { printf("bind %d [%s]:%s\n", fd, h, p); } else { printf("bind %d %s:%s\n", fd, h, p); } } r = bind(fd, ai->ai_addr, ai->ai_addrlen); if (r == -1) { twarn("bind()"); close(fd); continue; } r = listen(fd, 1024); if (r == -1) { twarn("listen()"); close(fd); continue; } break; } freeaddrinfo(airoot); if(ai == NULL) fd = -1; return fd; }
MAIN() { Sfio_t* fw; Sfio_t* fr; int fds[2]; int lseek_errno; int rv; if(!(fw = sfopen(NIL(Sfio_t*), tstfile(0), "w")) ) terror("Can't create temp file %s to write", tstfile(0)); if(!(fr = sfopen(NIL(Sfio_t*), tstfile(0), "r")) ) terror("Can't open temp file %s to read", tstfile(0)); sfseek(fr, (Sfoff_t)0, SEEK_END); if(sfgetc(fr) >= 0 || !sfeof(fr)) terror("Should have seen eof"); errno = 0; if((rv = sfwrite(fr, "a", 1)) == 1) terror("sfwrite returns %d, expecting 1", rv); if(errno != EBADF) twarn("Wrong errno %d after sfwrite(%d), expecting %d",errno,rv,EBADF); /* on some system (eg, apple), lseek does not set errno for this case */ errno = 0; lseek(sffileno(fw), (off_t)(-2), SEEK_SET); lseek_errno = errno; lseek(sffileno(fw), (off_t)0, SEEK_SET); errno = 0; if(sfseek(fw, (Sfoff_t)(-2), SEEK_SET) != (Sfoff_t)(-1) ) terror("sfseek should have failed"); if(errno != lseek_errno) twarn("Wrong errno %d after sfseek, expecting %d", errno, lseek_errno); errno = 0; if(sfseek(fw, (Sfoff_t)0, SEEK_SET|SEEK_CUR|SEEK_END) >= 0) terror("sfseek should not have succeeded"); if(errno != EINVAL) twarn("Wrong errno %d after sfseek, expecting %d", errno, EINVAL); if(pipe(fds) < 0) terror("Can't create pipes"); if(!(fw = sfnew(fw, NIL(Void_t*), (size_t)SF_UNBOUND, fds[1], SF_WRITE)) ) terror("Can't create stream for pipe"); errno = 0; if(sfseek(fw, (Sfoff_t)0, SEEK_SET) >= 0) terror("sfseek should have failed on a pipe"); if(errno != ESPIPE) twarn("Wrong errno %d after sfseek, expecting %d", ESPIPE); close(sffileno(fw)); errno = 0; if(sfseek(fw, (Sfoff_t)0, SEEK_END) >= 0) terror("sfseek should have failed on a closed file descriptor"); if(errno != EBADF) twarn("Wrong errno %d after sfseek, expecting %d", EBADF); TSTEXIT(0); }