static int mod_detach(void *instance) { rlm_unbound_t *inst = instance; if (inst->log_fd >= 0) { fr_event_fd_delete(inst->el, inst->log_fd, FR_EVENT_FILTER_IO); if (inst->ub) { ub_process(inst->ub); /* This can hang/leave zombies currently * see upstream bug #519 * ...so expect valgrind to complain with -m */ #if 0 ub_ctx_delete(inst->ub); #endif } } if (inst->log_pipe_stream[1]) { fclose(inst->log_pipe_stream[1]); } if (inst->log_pipe_stream[0]) { if (inst->log_pipe_in_use) { fr_event_fd_delete(inst->el, inst->log_pipe[0], FR_EVENT_FILTER_IO); } fclose(inst->log_pipe_stream[0]); } if (inst->log_stream) { fclose(inst->log_stream); } return 0; }
/* * Even when run in asyncronous mode, callbacks sent to libunbound still * must be run in an application-side thread (via ub_process.) This is * probably to keep the API usage consistent across threaded and forked * embedded client modes. This callback function lets an event loop call * ub_process when the instance's file descriptor becomes ready. */ static void ub_fd_handler(UNUSED fr_event_list_t *el, UNUSED int sock, UNUSED int flags, void *ctx) { rlm_unbound_t *inst = ctx; int err; err = ub_process(inst->ub); if (err) { ERROR("Async ub_process: %s", ub_strerror(err)); } }
static int ub_common_wait(rlm_unbound_t const *inst, REQUEST *request, char const *name, struct ub_result **ub, int async_id) { useconds_t iv, waited; iv = inst->timeout > 64 ? 64000 : inst->timeout * 1000; ub_process(inst->ub); for (waited = 0; (void const *)*ub == (void const *)inst; waited += iv, iv *= 2) { if (waited + iv > (useconds_t)inst->timeout * 1000) { usleep(inst->timeout * 1000 - waited); ub_process(inst->ub); break; } usleep(iv); /* Check if already handled by event loop */ if ((void const *)*ub != (void const *)inst) { break; } /* In case we are running single threaded */ ub_process(inst->ub); } if ((void const *)*ub == (void const *)inst) { int res; REDEBUG2("%s - DNS took too long", name); res = ub_cancel(inst->ub, async_id); if (res) { REDEBUG("%s - ub_cancel: %s", name, ub_strerror(res)); } return -1; } return 0; }
int main(void) { struct ub_ctx* ctx; volatile int qlen = 0; int retval; char qname[QNAME_MAX]; char *moredata, *nl; /* basic checks */ if(QUEUE_MAX < 1) { printf("error: queue length must be >0\n"); return 1; } /* create context */ ctx = ub_ctx_create(); if(!ctx) { printf("error: could not create unbound context\n"); return 1; } fprintf(stderr, "HOST;ERR;RCODE;DATA;NXDOMAIN\n"); /* we keep running, lets do something while waiting */ moredata = (char*)-1; do { /* queue has room && more data on stdin? */ if(qlen < QUEUE_MAX && moredata) { /* read and prepare qname from stdin */ moredata = fgets(qname, QNAME_MAX, stdin); if(moredata != NULL) { nl = strrchr(qname, '\n'); if(nl) *nl = '\0'; if((int)nl == (int)&qname) { printf("empty input\n"); continue; } /* add async query to queue */ retval = ub_resolve_async(ctx, qname, 1, 1, (void*)&qlen, mycallback, NULL); if(retval != 0) { printf("resolve error for %s: %s\n", qname, ub_strerror(retval)); continue; } qlen++; } usleep(50000); /* wait 1/50 of a second */ } else { /* queue is full || eof stdin reached */ usleep(100000); /* wait 1/10 of a second */ retval = ub_process(ctx); if(retval != 0) { printf("resolve error: %s\n", ub_strerror(retval)); return 1; } } } while(qlen || moredata); ub_ctx_delete(ctx); return 0; }
/** main program for asynclook */ int main(int argc, char** argv) { int c; struct ub_ctx* ctx; struct lookinfo* lookups; int i, r, cancel=0, blocking=0, ext=0; /* init log now because solaris thr_key_create() is not threadsafe */ log_init(0,0,0); /* lock debug start (if any) */ checklock_start(); /* create context */ ctx = ub_ctx_create(); if(!ctx) { printf("could not create context, %s\n", strerror(errno)); return 1; } /* command line options */ if(argc == 1) { usage(argv); } while( (c=getopt(argc, argv, "bcdf:hH:r:tx")) != -1) { switch(c) { case 'd': r = ub_ctx_debuglevel(ctx, 3); checkerr("ub_ctx_debuglevel", r); break; case 't': r = ub_ctx_async(ctx, 1); checkerr("ub_ctx_async", r); break; case 'c': cancel = 1; break; case 'b': blocking = 1; break; case 'r': r = ub_ctx_resolvconf(ctx, optarg); if(r != 0) { printf("ub_ctx_resolvconf " "error: %s : %s\n", ub_strerror(r), strerror(errno)); return 1; } break; case 'H': r = ub_ctx_hosts(ctx, optarg); if(r != 0) { printf("ub_ctx_hosts " "error: %s : %s\n", ub_strerror(r), strerror(errno)); return 1; } break; case 'f': r = ub_ctx_set_fwd(ctx, optarg); checkerr("ub_ctx_set_fwd", r); break; case 'x': ext = 1; break; case 'h': case '?': default: usage(argv); } } argc -= optind; argv += optind; if(ext) return ext_test(ctx, argc, argv); /* allocate array for results. */ lookups = (struct lookinfo*)calloc((size_t)argc, sizeof(struct lookinfo)); if(!lookups) { printf("out of memory\n"); return 1; } /* perform asynchronous calls */ num_wait = argc; for(i=0; i<argc; i++) { lookups[i].name = argv[i]; if(blocking) { fprintf(stderr, "lookup %s\n", argv[i]); r = ub_resolve(ctx, argv[i], LDNS_RR_TYPE_A, LDNS_RR_CLASS_IN, &lookups[i].result); checkerr("ub_resolve", r); } else { fprintf(stderr, "start async lookup %s\n", argv[i]); r = ub_resolve_async(ctx, argv[i], LDNS_RR_TYPE_A, LDNS_RR_CLASS_IN, &lookups[i], &lookup_is_done, &lookups[i].async_id); checkerr("ub_resolve_async", r); } } if(blocking) num_wait = 0; else if(cancel) { for(i=0; i<argc; i++) { fprintf(stderr, "cancel %s\n", argv[i]); r = ub_cancel(ctx, lookups[i].async_id); if(r != UB_NOID) checkerr("ub_cancel", r); } num_wait = 0; } /* wait while the hostnames are looked up. Do something useful here */ if(num_wait > 0) for(i=0; i<1000; i++) { usleep(100000); fprintf(stderr, "%g seconds passed\n", 0.1*(double)i); r = ub_process(ctx); checkerr("ub_process", r); if(num_wait == 0) break; } if(i>=999) { printf("timed out\n"); return 0; } printf("lookup complete\n"); /* print lookup results */ for(i=0; i<argc; i++) { print_result(&lookups[i]); ub_resolve_free(lookups[i].result); } ub_ctx_delete(ctx); free(lookups); checklock_stop(); return 0; }