void virusaction(const char *filename, const char *virname, const struct cfgstruct *copt) { pid_t pid; struct cfgstruct *cpt; if(!(cpt = cfgopt(copt, "VirusEvent"))) return; /* NB: we need to fork here since this function modifies the environment. (Modifications to the env. are not reentrant, but we need to be.) */ pid = fork(); if ( pid == 0 ) { /* child... */ char *buffer, *pt, *cmd; cmd = strdup(cpt->strarg); if((pt = strstr(cmd, "%v"))) { buffer = (char *) mcalloc(strlen(cmd) + strlen(virname) + 10, sizeof(char)); *pt = 0; pt += 2; strcpy(buffer, cmd); strcat(buffer, virname); strcat(buffer, pt); free(cmd); cmd = strdup(buffer); free(buffer); } /* Allocate env vars.. to be portable env vars should not be freed */ buffer = (char *) mcalloc(strlen(ENV_FILE) + strlen(filename) + 2, sizeof(char)); sprintf(buffer, "%s=%s", ENV_FILE, filename); putenv(buffer); buffer = (char *) mcalloc(strlen(ENV_VIRUS) + strlen(virname) + 2, sizeof(char)); sprintf(buffer, "%s=%s", ENV_VIRUS, virname); putenv(buffer); /* WARNING: this is uninterruptable ! */ exit(system(cmd)); /* The below is not reached but is here for completeness to remind maintainers that this buffer is still allocated.. */ free(cmd); } else if (pid > 0) { /* parent */ waitpid(pid, NULL, 0); } else { /* error.. */ logg("!VirusAction: fork failed.\n"); } }
int main(int argc, char *argv[]) { int *i; char c; OPTIND = 1; printf("main %d\n", argc); while ((c = cfgopt(argc, argv, "abc:d:012")) != -1) { //while ((c = cfgopt_long(argc, argv, "abc:d:012", opts, i)) != -1) { //while ((c = cfgopt_long(argc, argv, "", opts, i)) != -1) { switch(c) { default: printf("cfgopt %c, optarg %s\n", c, OPTARG?OPTARG:"NULL"); } printf("-- loop: %c optind %d, optarg [%s] [%c]\n", c, OPTIND, OPTARG, OPTOPT); } return 0; }
int command(int desc, const struct cl_node *root, const struct cl_limits *limits, int options, const struct cfgstruct *copt, int timeout) { char buff[1025]; int bread, opt, retval; struct cfgstruct *cpt; retval = poll_fd(desc, timeout); switch (retval) { case 0: /* timeout */ return -2; case -1: mdprintf(desc, "ERROR\n"); logg("!Command: poll_fd failed.\n"); return -1; } while((bread = readsock(desc, buff, 1024)) == -1 && errno == EINTR); if(bread == 0) { /* Connection closed */ return -1; } if(bread < 0) { logg("!Command parser: read() failed.\n"); /* at least try to display this error message */ /* mdprintf(desc, "ERROR: Command parser: read() failed.\n"); */ return -1; } buff[bread] = 0; cli_chomp(buff); if(!strncmp(buff, CMD1, strlen(CMD1))) { /* SCAN */ if(scan(buff + strlen(CMD1) + 1, NULL, root, limits, options, copt, desc, 0) == -2) if(cfgopt(copt, "ExitOnOOM")) return COMMAND_SHUTDOWN; } else if(!strncmp(buff, CMD2, strlen(CMD2))) { /* RAWSCAN */ opt = options & ~CL_SCAN_ARCHIVE; if(scan(buff + strlen(CMD2) + 1, NULL, root, NULL, opt, copt, desc, 0) == -2) if(cfgopt(copt, "ExitOnOOM")) return COMMAND_SHUTDOWN; } else if(!strncmp(buff, CMD3, strlen(CMD3))) { /* QUIT */ return COMMAND_SHUTDOWN; } else if(!strncmp(buff, CMD4, strlen(CMD4))) { /* RELOAD */ mdprintf(desc, "RELOADING\n"); return COMMAND_RELOAD; } else if(!strncmp(buff, CMD5, strlen(CMD5))) { /* PING */ mdprintf(desc, "PONG\n"); } else if(!strncmp(buff, CMD6, strlen(CMD6))) { /* CONTSCAN */ if(scan(buff + strlen(CMD6) + 1, NULL, root, limits, options, copt, desc, 1) == -2) if(cfgopt(copt, "ExitOnOOM")) return COMMAND_SHUTDOWN; } else if(!strncmp(buff, CMD7, strlen(CMD7))) { /* VERSION */ const char *dbdir; char *path; struct cl_cvd *daily; if((cpt = cfgopt(copt, "DatabaseDirectory")) || (cpt = cfgopt(copt, "DataDirectory"))) dbdir = cpt->strarg; else dbdir = cl_retdbdir(); if(!(path = mmalloc(strlen(dbdir) + 11))) { mdprintf(desc, "Memory allocation error - SHUTDOWN forced\n"); return COMMAND_SHUTDOWN; } sprintf(path, "%s/daily.cvd", dbdir); if((daily = cl_cvdhead(path))) { time_t t = (time_t) daily->stime; pthread_mutex_lock(&ctime_mutex); mdprintf(desc, "ClamAV "VERSION"/%d/%s", daily->version, ctime(&t)); pthread_mutex_unlock(&ctime_mutex); cl_cvdfree(daily); } else { mdprintf(desc, "ClamAV "VERSION"\n"); } free(path); } else if(!strncmp(buff, CMD8, strlen(CMD8))) { /* STREAM */ if(scanstream(desc, NULL, root, limits, options, copt) == CL_EMEM) if(cfgopt(copt, "ExitOnOOM")) return COMMAND_SHUTDOWN; } else if(!strncmp(buff, CMD9, strlen(CMD9))) { /* SESSION */ return COMMAND_SESSION; } else if(!strncmp(buff, CMD10, strlen(CMD10))) { /* END */ return COMMAND_END; } else if(!strncmp(buff, CMD11, strlen(CMD11))) { /* SHUTDOWN */ return COMMAND_SHUTDOWN; } else if(!strncmp(buff, CMD12, strlen(CMD12))) { /* FD */ int fd = atoi(buff + strlen(CMD12) + 1); scanfd(fd, NULL, root, limits, options, copt, desc, 0); close(fd); /* FIXME: should we close it here? */ } else { mdprintf(desc, "UNKNOWN COMMAND\n"); } return 0; /* no error and no 'special' command executed */ }
void scanner_thread(void *arg) { client_conn_t *conn = (client_conn_t *) arg; sigset_t sigset; int ret, timeout, session=FALSE; struct cfgstruct *cpt; /* ignore all signals */ sigfillset(&sigset); pthread_sigmask(SIG_SETMASK, &sigset, NULL); if((cpt = cfgopt(conn->copt, "ReadTimeout"))) { timeout = cpt->numarg; } else { timeout = CL_DEFAULT_SCANTIMEOUT; } if(!timeout) timeout = -1; do { ret = command(conn->sd, conn->root, conn->limits, conn->options, conn->copt, timeout); if (ret < 0) { break; } switch(ret) { case COMMAND_SHUTDOWN: pthread_mutex_lock(&exit_mutex); progexit = 1; kill(conn->mainpid, SIGTERM); pthread_mutex_unlock(&exit_mutex); break; case COMMAND_RELOAD: pthread_mutex_lock(&reload_mutex); reload = 1; pthread_mutex_unlock(&reload_mutex); break; case COMMAND_SESSION: session = TRUE; break; case COMMAND_END: session = FALSE; break; } if (session) { pthread_mutex_lock(&exit_mutex); if(progexit) { session = FALSE; } pthread_mutex_unlock(&exit_mutex); pthread_mutex_lock(&reload_mutex); if (conn->root_timestamp != reloaded_time) { session = FALSE; } pthread_mutex_unlock(&reload_mutex); } } while (session); close(conn->sd); cl_free(conn->root); free(conn); return; }
int acceptloop_th(int socketd, struct cl_node *root, const struct cfgstruct *copt) { int new_sd, max_threads, stdopt; unsigned int options = 0; threadpool_t *thr_pool; struct sigaction sigact; mode_t old_umask; struct cl_limits limits; sigset_t sigset; client_conn_t *client_conn; struct cfgstruct *cpt; #ifdef HAVE_STRERROR_R char buff[BUFFSIZE + 1]; #endif unsigned int selfchk; time_t start_time, current_time; pid_t mainpid; int idletimeout; #if defined(C_BIGSTACK) || defined(C_BSD) size_t stacksize; #endif #ifdef CLAMUKO pthread_t clamuko_pid; pthread_attr_t clamuko_attr; struct thrarg *tharg = NULL; /* shut up gcc */ #endif memset(&sigact, 0, sizeof(struct sigaction)); /* save the PID */ mainpid = getpid(); if((cpt = cfgopt(copt, "PidFile"))) { FILE *fd; old_umask = umask(0006); if((fd = fopen(cpt->strarg, "w")) == NULL) { logg("!Can't save PID in file %s\n", cpt->strarg); } else { fprintf(fd, "%d", (int) mainpid); fclose(fd); } umask(old_umask); } logg("*Listening daemon: PID: %d\n", getpid()); if((cpt = cfgopt(copt, "MaxThreads"))) { max_threads = cpt->numarg; } else { max_threads = CL_DEFAULT_MAXTHREADS; } if(cfgopt(copt, "DisableDefaultScanOptions")) { logg("RECOMMENDED OPTIONS DISABLED.\n"); stdopt = 0; } else { options |= CL_SCAN_STDOPT; stdopt = 1; } if(stdopt || cfgopt(copt, "ScanArchive") || cfgopt(copt, "ClamukoScanArchive")) { /* set up limits */ memset(&limits, 0, sizeof(struct cl_limits)); if((cpt = cfgopt(copt, "ArchiveMaxFileSize"))) { if((limits.maxfilesize = cpt->numarg)) { logg("Archive: Archived file size limit set to %d bytes.\n", limits.maxfilesize); } else { logg("^Archive: File size limit protection disabled.\n"); } } else { limits.maxfilesize = 10485760; logg("Archive: Archived file size limit set to %d bytes.\n", limits.maxfilesize); } if((cpt = cfgopt(copt, "ArchiveMaxRecursion"))) { if((limits.maxreclevel = cpt->numarg)) { logg("Archive: Recursion level limit set to %d.\n", limits.maxreclevel); } else { logg("^Archive: Recursion level limit protection disabled.\n"); } } else { limits.maxreclevel = 8; logg("Archive: Recursion level limit set to %d.\n", limits.maxreclevel); } if((cpt = cfgopt(copt, "ArchiveMaxFiles"))) { if((limits.maxfiles = cpt->numarg)) { logg("Archive: Files limit set to %d.\n", limits.maxfiles); } else { logg("^Archive: Files limit protection disabled.\n"); } } else { limits.maxfiles = 1000; logg("Archive: Files limit set to %d.\n", limits.maxfiles); } if((cpt = cfgopt(copt, "ArchiveMaxCompressionRatio"))) { if((limits.maxratio = cpt->numarg)) { logg("Archive: Compression ratio limit set to %d.\n", limits.maxratio); } else { logg("^Archive: Compression ratio limit disabled.\n"); } } else { limits.maxratio = 250; logg("Archive: Compression ratio limit set to %d.\n", limits.maxratio); } if(cfgopt(copt, "ArchiveLimitMemoryUsage")) { limits.archivememlim = 1; logg("Archive: Limited memory usage.\n"); } else { limits.archivememlim = 0; } } if(stdopt || cfgopt(copt, "ScanArchive")) { logg("Archive support enabled.\n"); options |= CL_SCAN_ARCHIVE; if(cfgopt(copt, "ScanRAR")) { logg("Archive: RAR support enabled.\n"); } else { logg("Archive: RAR support disabled.\n"); options |= CL_SCAN_DISABLERAR; } if(cfgopt(copt, "ArchiveBlockEncrypted")) { logg("Archive: Blocking encrypted archives.\n"); options |= CL_SCAN_BLOCKENCRYPTED; } if(cfgopt(copt, "ArchiveBlockMax")) { logg("Archive: Blocking archives that exceed limits.\n"); options |= CL_SCAN_BLOCKMAX; } } else { logg("Archive support disabled.\n"); } if(stdopt || cfgopt(copt, "ScanPE")) { logg("Portable Executable support enabled.\n"); options |= CL_SCAN_PE; if(cfgopt(copt, "DetectBrokenExecutables")) { logg("Detection of broken executables enabled.\n"); options |= CL_SCAN_BLOCKBROKEN; } } else { logg("Portable Executable support disabled.\n"); } if(stdopt || cfgopt(copt, "ScanMail")) { logg("Mail files support enabled.\n"); options |= CL_SCAN_MAIL; if(cfgopt(copt, "MailFollowURLs")) { logg("Mail: URL scanning enabled.\n"); options |= CL_SCAN_MAILURL; } } else { logg("Mail files support disabled.\n"); } if(stdopt || cfgopt(copt, "ScanOLE2")) { logg("OLE2 support enabled.\n"); options |= CL_SCAN_OLE2; } else { logg("OLE2 support disabled.\n"); } if(stdopt || cfgopt(copt, "ScanHTML")) { logg("HTML support enabled.\n"); options |= CL_SCAN_HTML; } else { logg("HTML support disabled.\n"); } if((cpt = cfgopt(copt, "SelfCheck"))) { selfchk = cpt->numarg; } else { selfchk = CL_DEFAULT_SELFCHECK; } if(!selfchk) { logg("Self checking disabled.\n"); } else { logg("Self checking every %d seconds.\n", selfchk); } if(cfgopt(copt, "ClamukoScanOnLine") || cfgopt(copt, "ClamukoScanOnAccess")) #ifdef CLAMUKO { pthread_attr_init(&clamuko_attr); pthread_attr_setdetachstate(&clamuko_attr, PTHREAD_CREATE_JOINABLE); tharg = (struct thrarg *) mmalloc(sizeof(struct thrarg)); tharg->copt = copt; tharg->root = root; tharg->limits = &limits; tharg->options = options; pthread_create(&clamuko_pid, &clamuko_attr, clamukoth, tharg); } #else logg("Clamuko is not available.\n"); #endif /* set up signal handling */ sigfillset(&sigset); sigdelset(&sigset, SIGINT); sigdelset(&sigset, SIGTERM); sigdelset(&sigset, SIGSEGV); sigdelset(&sigset, SIGHUP); sigdelset(&sigset, SIGPIPE); sigdelset(&sigset, SIGUSR2); sigprocmask(SIG_SETMASK, &sigset, NULL); /* SIGINT, SIGTERM, SIGSEGV */ sigact.sa_handler = sighandler_th; sigemptyset(&sigact.sa_mask); sigaddset(&sigact.sa_mask, SIGINT); sigaddset(&sigact.sa_mask, SIGTERM); sigaddset(&sigact.sa_mask, SIGHUP); sigaddset(&sigact.sa_mask, SIGPIPE); sigaddset(&sigact.sa_mask, SIGUSR2); sigaction(SIGINT, &sigact, NULL); sigaction(SIGTERM, &sigact, NULL); sigaction(SIGHUP, &sigact, NULL); sigaction(SIGPIPE, &sigact, NULL); sigaction(SIGUSR2, &sigact, NULL); if(!debug_mode) { sigaddset(&sigact.sa_mask, SIGHUP); sigaction(SIGSEGV, &sigact, NULL); } pthread_mutex_init(&exit_mutex, NULL); pthread_mutex_init(&reload_mutex, NULL); if((cpt = cfgopt(copt, "IdleTimeout"))) idletimeout = cpt->numarg; else idletimeout = CL_DEFAULT_IDLETIMEOUT; if((thr_pool=thrmgr_new(max_threads, idletimeout, scanner_thread)) == NULL) { logg("!thrmgr_new failed\n"); exit(-1); } time(&start_time); for(;;) { new_sd = accept(socketd, NULL, NULL); if((new_sd == -1) && (errno != EINTR)) { /* very bad - need to exit or restart */ #ifdef HAVE_STRERROR_R logg("!accept() failed: %s\n", strerror_r(errno, buff, BUFFSIZE)); #else logg("!accept() failed\n"); #endif continue; } if (sighup) { logg("SIGHUP caught: re-opening log file.\n"); logg_close(); sighup = 0; if(!logg_file && (cpt = cfgopt(copt, "LogFile"))) logg_file = cpt->strarg; } if (!progexit && new_sd >= 0) { client_conn = (client_conn_t *) mmalloc(sizeof(struct client_conn_tag)); client_conn->sd = new_sd; client_conn->options = options; client_conn->copt = copt; client_conn->root = cl_dup(root); client_conn->root_timestamp = reloaded_time; client_conn->limits = &limits; client_conn->mainpid = mainpid; if (!thrmgr_dispatch(thr_pool, client_conn)) { close(client_conn->sd); free(client_conn); logg("!thread dispatch failed\n"); } } pthread_mutex_lock(&exit_mutex); if(progexit) { if (new_sd >= 0) { close(new_sd); } pthread_mutex_unlock(&exit_mutex); break; } pthread_mutex_unlock(&exit_mutex); if(selfchk) { time(¤t_time); if((current_time - start_time) > (time_t)selfchk) { if(reload_db(root, copt, TRUE)) { pthread_mutex_lock(&reload_mutex); reload = 1; pthread_mutex_unlock(&reload_mutex); } time(&start_time); } } pthread_mutex_lock(&reload_mutex); if(reload) { pthread_mutex_unlock(&reload_mutex); root = reload_db(root, copt, FALSE); pthread_mutex_lock(&reload_mutex); reload = 0; time(&reloaded_time); pthread_mutex_unlock(&reload_mutex); #ifdef CLAMUKO if(cfgopt(copt, "ClamukoScanOnLine") || cfgopt(copt, "ClamukoScanOnAccess")) { logg("Stopping and restarting Clamuko.\n"); pthread_kill(clamuko_pid, SIGUSR1); pthread_join(clamuko_pid, NULL); tharg->root = root; pthread_create(&clamuko_pid, &clamuko_attr, clamukoth, tharg); } #endif } else { pthread_mutex_unlock(&reload_mutex); } } /* Destroy the thread manager. * This waits for all current tasks to end */ thrmgr_destroy(thr_pool); #ifdef CLAMUKO if(cfgopt(copt, "ClamukoScanOnLine") || cfgopt(copt, "ClamukoScanOnAccess")) { logg("Stopping Clamuko.\n"); pthread_kill(clamuko_pid, SIGUSR1); pthread_join(clamuko_pid, NULL); } #endif cl_free(root); logg("*Shutting down the main socket.\n"); shutdown(socketd, 2); logg("*Closing the main socket.\n"); close(socketd); #ifndef C_OS2 if((cpt = cfgopt(copt, "LocalSocket"))) { if(unlink(cpt->strarg) == -1) logg("!Can't unlink the socket file %s\n", cpt->strarg); else logg("Socket file removed.\n"); } #endif if((cpt = cfgopt(copt, "PidFile"))) { if(unlink(cpt->strarg) == -1) logg("!Can't unlink the pid file %s\n", cpt->strarg); else logg("Pid file removed.\n"); } logg("Exiting (clean)\n"); time(¤t_time); logg("--- Stopped at %s", ctime(¤t_time)); return 0; }
static struct cl_node *reload_db(struct cl_node *root, const struct cfgstruct *copt, int do_check) { const char *dbdir; int retval; unsigned int virnum = 0; struct cfgstruct *cpt; static struct cl_stat *dbstat=NULL; if(do_check) { if(dbstat == NULL) { logg("No stats for Database check - forcing reload\n"); return root; } if(cl_statchkdir(dbstat) == 1) { logg("SelfCheck: Database modification detected. Forcing reload.\n"); return root; } else { logg("SelfCheck: Database status OK.\n"); return NULL; } } /* release old structure */ if(root) { cl_free(root); root = NULL; } if((cpt = cfgopt(copt, "DatabaseDirectory")) || (cpt = cfgopt(copt, "DataDirectory"))) { dbdir = cpt->strarg; } else { dbdir = cl_retdbdir(); } logg("Reading databases from %s\n", dbdir); if(dbstat == NULL) { dbstat = (struct cl_stat *) mmalloc(sizeof(struct cl_stat)); } else { cl_statfree(dbstat); } memset(dbstat, 0, sizeof(struct cl_stat)); cl_statinidir(dbdir, dbstat); if((retval = cl_loaddbdir(dbdir, &root, &virnum))) { logg("!reload db failed: %s\n", cl_strerror(retval)); exit(-1); } if(!root) { logg("!load db failed: %s\n", cl_strerror(retval)); exit(-1); } if((retval = cl_build(root)) != 0) { logg("!Database initialization error: can't build engine: %s\n", cl_strerror(retval)); exit(-1); } logg("Database correctly reloaded (%d viruses)\n", virnum); return root; }
int dconnect(const struct optstruct *opt) { struct sockaddr_un server; struct sockaddr_in server2; struct hostent *he; struct cfgstruct *copt, *cpt; const char *clamav_conf = getargl(opt, "config-file"); int sockd; if(!clamav_conf) clamav_conf = DEFAULT_CFG; if((copt = parsecfg(clamav_conf, 1)) == NULL) { mprintf("@Can't parse the configuration file.\n"); return -1; } memset((char *) &server, 0, sizeof(server)); memset((char *) &server2, 0, sizeof(server2)); /* Set default address to connect to */ server2.sin_addr.s_addr = inet_addr("127.0.0.1"); if(cfgopt(copt, "TCPSocket") && cfgopt(copt, "LocalSocket")) { mprintf("@Clamd is not configured properly.\n"); return -1; } else if((cpt = cfgopt(copt, "LocalSocket"))) { server.sun_family = AF_UNIX; strncpy(server.sun_path, cpt->strarg, sizeof(server.sun_path)); if((sockd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { perror("socket()"); mprintf("@Can't create the socket.\n"); return -1; } if(connect(sockd, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) { close(sockd); perror("connect()"); mprintf("@Can't connect to clamd.\n"); return -1; } } else if((cpt = cfgopt(copt, "TCPSocket"))) { if((sockd = socket(SOCKET_INET, SOCK_STREAM, 0)) < 0) { perror("socket()"); mprintf("@Can't create the socket.\n"); return -1; } server2.sin_family = AF_INET; server2.sin_port = htons(cpt->numarg); if((cpt = cfgopt(copt, "TCPAddr"))) { if ((he = gethostbyname(cpt->strarg)) == 0) { close(sockd); perror("gethostbyname()"); mprintf("@Can't lookup clamd hostname.\n"); return -1; } server2.sin_addr = *(struct in_addr *) he->h_addr_list[0]; } if(connect(sockd, (struct sockaddr *) &server2, sizeof(struct sockaddr_in)) < 0) { close(sockd); perror("connect()"); mprintf("@Can't connect to clamd.\n"); return -1; } } else { mprintf("@Clamd is not configured properly.\n"); return -1; } return sockd; }
int localserver(const struct optstruct *opt, const struct cfgstruct *copt, struct cl_node *root) { struct sockaddr_un server; int sockfd, backlog; struct cfgstruct *cpt; struct stat foo; char *estr; memset((char *) &server, 0, sizeof(server)); server.sun_family = AF_UNIX; strncpy(server.sun_path, cfgopt(copt, "LocalSocket")->strarg, sizeof(server.sun_path)); if((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { estr = strerror(errno); /* fprintf(stderr, "ERROR: socket() error: %s\n", estr); */ logg("!Socket allocation error: %s\n", estr); exit(1); } if(bind(sockfd, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) == -1) { if(errno == EADDRINUSE) { if(connect(sockfd, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) >= 0) { close(sockfd); logg("!Socket file %s is in use by another process.\n", server.sun_path); exit(1); } if(cfgopt(copt, "FixStaleSocket")) { logg("^Socket file %s exists. Unclean shutdown? Removing...\n", server.sun_path); if(unlink(server.sun_path) == -1) { estr = strerror(errno); logg("!Socket file %s could not be removed: %s\n", server.sun_path, estr); exit(1); } if(bind(sockfd, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) == -1) { estr = strerror(errno); logg("!Socket file %s could not be bound: %s (unlink tried)\n", server.sun_path, estr); exit(1); } } else if(stat(server.sun_path, &foo) != -1) { logg("!Socket file %s exists. Either remove it, or configure a different one.\n", server.sun_path); exit(1); } } else { estr = strerror(errno); logg("!Socket file %s could not be bound: %s\n", server.sun_path, estr); exit(1); } } logg("Unix socket file %s\n", server.sun_path); if((cpt = cfgopt(copt, "MaxConnectionQueueLength"))) backlog = cpt->numarg; else backlog = CL_DEFAULT_BACKLOG; logg("Setting connection queue length to %d\n", backlog); if(listen(sockfd, backlog) == -1) { estr = strerror(errno); /* fprintf(stderr, "ERROR: listen() error: %s\n", estr); */ logg("!listen() error: %s\n", estr); exit(1); } acceptloop_th(sockfd, root, copt); return 0; }