static int open_ioctl_fd(int dev_autofs_fd, const char *where, dev_t devid) { struct autofs_dev_ioctl *param; size_t l; assert(dev_autofs_fd >= 0); assert(where); l = sizeof(struct autofs_dev_ioctl) + strlen(where) + 1; param = alloca(l); init_autofs_dev_ioctl(param); param->size = l; param->ioctlfd = -1; param->openmount.devid = devid; strcpy(param->path, where); if (ioctl(dev_autofs_fd, AUTOFS_DEV_IOCTL_OPENMOUNT, param) < 0) return -errno; if (param->ioctlfd < 0) return -EIO; (void) fd_cloexec(param->ioctlfd, true); return param->ioctlfd; }
void become_daemon (void) { pid_t pid; int nullfd = open ("/dev/null", O_RDWR, 0); if (nullfd < 0) w_die ("cannot daemonize, unable to open '/dev/null': $E\n"); fd_cloexec (nullfd); if (dup2 (nullfd, STDIN_FILENO) < 0) w_die ("cannot daemonize, unable to redirect stdin: $E\n"); if (dup2 (nullfd, STDOUT_FILENO) < 0) w_die ("cannot daemonize, unable to redirect stdout: $E\n"); if (dup2 (nullfd, STDERR_FILENO) < 0) w_die ("cannot daemonize, unable to redirect stderr: $E\n"); pid = fork (); if (pid < 0) w_die ("cannot daemonize: $E\n"); if (pid > 0) _exit (EXIT_SUCCESS); if (setsid () == -1) _exit (111); }
int fdset_cloexec(FDSet *fds, bool b) { Iterator i; void *p; int r; assert(fds); SET_FOREACH(p, MAKE_SET(fds), i) { r = fd_cloexec(PTR_TO_FD(p), b); if (r < 0) return r; }
static int open_sockets(int *epoll_fd, bool accept) { char **address; int n, fd, r; int count = 0; n = sd_listen_fds(true); if (n < 0) { log_error("Failed to read listening file descriptors from environment: %s", strerror(-n)); return n; } if (n > 0) { log_info("Received %i descriptors via the environment.", n); for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) { r = fd_cloexec(fd, arg_accept); if (r < 0) return r; count ++; } } /* Close logging and all other descriptors */ if (arg_listen) { int except[3 + n]; for (fd = 0; fd < SD_LISTEN_FDS_START + n; fd++) except[fd] = fd; log_close(); close_all_fds(except, 3 + n); } /** Note: we leak some fd's on error here. I doesn't matter * much, since the program will exit immediately anyway, but * would be a pain to fix. */ STRV_FOREACH(address, arg_listen) { fd = make_socket_fd(*address, SOCK_STREAM | (arg_accept*SOCK_CLOEXEC)); if (fd < 0) { log_open(); log_error("Failed to open '%s': %s", *address, strerror(-fd)); return fd; } assert(fd == SD_LISTEN_FDS_START + count); count ++; }
/* do_choom() ** if opt_e: abort on fail ** no chdir(), no effect to cwd */ static void do_choom(void) { int fd; /* comment: ** normally we would first create/write a tmpfile ** then atomically rename() tmpfile into procfile ** but open(...,O_CREAT,...) of a new tmpfile on /proc fails (EEXIST) ** leaving no choice but to write into the procfile directly */ if((fd = open(pathbuf, O_WRONLY | O_TRUNC, 0)) == -1){ if(opt_e){ fatal_syserr("failure on open() for path: ", pathbuf); } /* else: */ if(opt_v){ syserr_warn("ignoring failure on open() for path: ", pathbuf); } return; } fd_cloexec(fd); if(write_all(fd, setbuf, cstr_len(setbuf)) == -1){ if(opt_e){ fatal_syserr("failure on write() to path: ", pathbuf); } /* else: */ if(opt_v){ syserr_warn("ignoring failure on write() to path: ", pathbuf); } return; } /* comment: ** fsync() on /proc fails (EINVAL) ** so we simply ignore any following errors */ fsync(fd); close(fd); /* success: */ if(opt_v){ eputs(progname, ": successfully configured ", pathbuf); } return; }
int make_stdio(int fd) { int r, s, t; assert(fd >= 0); r = dup2(fd, STDIN_FILENO); s = dup2(fd, STDOUT_FILENO); t = dup2(fd, STDERR_FILENO); if (fd >= 3) safe_close(fd); if (r < 0 || s < 0 || t < 0) return -errno; /* Explicitly unset O_CLOEXEC, since if fd was < 3, then * dup2() was a NOP and the bit hence possibly set. */ fd_cloexec(STDIN_FILENO, false); fd_cloexec(STDOUT_FILENO, false); fd_cloexec(STDERR_FILENO, false); return 0; }
static void setup(void) { int i = 0; if(pipe(my_sigpipe) == -1){ fatal_syserr("failure setting up selfpipe"); } for(i = 0; i < 2; ++i){ fd_cloexec(my_sigpipe[i]); fd_nonblock(my_sigpipe[i]); } if(pipe(my_logpipe) == -1){ fatal_syserr("failure creating logpipe"); } sigset_fill(&my_sigset); sigset_block(&my_sigset); sig_catch(SIGTERM, &sigtrap); sig_catch(SIGINT, &sigtrap); sig_catch(SIGCHLD, &sigtrap); /* catching these signals to pass to program: */ sig_catch(SIGALRM, &sigtrap); sig_catch(SIGCONT, &sigtrap); sig_catch(SIGHUP, &sigtrap); sig_catch(SIGQUIT, &sigtrap); sig_catch(SIGTSTP, &sigtrap); sig_catch(SIGUSR1, &sigtrap); sig_catch(SIGUSR2, &sigtrap); sig_ignore(SIGPIPE); return; }
void *epoll_init(EL_P loop) { EEPOLL_P ept = (EEPOLL_P)mm_malloc(sizeof(struct evt_epoll)); memset(ept, 0, sizeof(struct evt_epoll)); /* use epoll_create1 first */ #ifdef EPOLL_CLOEXEC ept->fd = epoll_create1(EPOLL_CLOEXEC); #else ept->fd = epoll_create(1); if (ept->fd >= 0) fd_cloexec(ept->fd); #endif if (ept->fd < 0) { goto epoll_init_failed; } ept->feature = loop->poll_feature; ept->nevent = EPOLL_INIT_NEVENT; ept->event = (EP_EVT*)mm_malloc(sizeof(EP_EVT) * EPOLL_INIT_NEVENT); /* init fucntion pointer with loop*/ loop->poll_destroy = epoll_destroy; loop->poll_dispatch = epoll_dispatch; loop->poll_update = epoll_update; log_inner("epoll(%d) init complete!", ept->fd); return ept; epoll_init_failed: log_error("epoll init failed!"); if (ept->fd > 0) close (ept->fd); if (ept->event) mm_free(ept->event); mm_free(ept); return NULL; }
void stdio_unset_cloexec(void) { fd_cloexec(STDIN_FILENO, false); fd_cloexec(STDOUT_FILENO, false); fd_cloexec(STDERR_FILENO, false); }
static int add_epoll(int epoll_fd, int fd) { struct epoll_event ev = { .events = EPOLLIN, .data.fd = fd, }; assert(epoll_fd >= 0); assert(fd >= 0); if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) return log_error_errno(errno, "Failed to add event on epoll fd:%d for fd:%d: %m", epoll_fd, fd); return 0; } static int open_sockets(int *epoll_fd, bool accept) { char **address; int n, fd, r; int count = 0; n = sd_listen_fds(true); if (n < 0) return log_error_errno(n, "Failed to read listening file descriptors from environment: %m"); if (n > 0) { log_info("Received %i descriptors via the environment.", n); for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) { r = fd_cloexec(fd, arg_accept); if (r < 0) return r; count++; } } /* Close logging and all other descriptors */ if (arg_listen) { int except[3 + n]; for (fd = 0; fd < SD_LISTEN_FDS_START + n; fd++) except[fd] = fd; log_close(); close_all_fds(except, 3 + n); } /** Note: we leak some fd's on error here. I doesn't matter * much, since the program will exit immediately anyway, but * would be a pain to fix. */ STRV_FOREACH(address, arg_listen) { fd = make_socket_fd(LOG_DEBUG, *address, arg_socket_type, (arg_accept*SOCK_CLOEXEC)); if (fd < 0) { log_open(); return log_error_errno(fd, "Failed to open '%s': %m", *address); } assert(fd == SD_LISTEN_FDS_START + count); count++; }
/* perpd_svdef_activate() ** activate service definition ** called by perpd_scan() ** ** return: ** 0: success ** -1: failure ** ** notes: ** failures include: ** - service definition directory name too long ** (must me less than, say, 240 characters) ** - open() on service definition directory ** - pipe() for logpipe ** service is activated only on success */ int perpd_svdef_activate(struct svdef *svdef, const char *svdir, const struct stat *st_dir) { struct stat st; char path_buf[256]; int fd; perpd_svdef_clear(svdef); if(cstr_len(svdir) > 240){ errno = ENAMETOOLONG; warn_syserr("service definition directory name error: ", svdir); return -1; } svdef->dev = st_dir->st_dev; svdef->ino = st_dir->st_ino; cstr_lcpy(svdef->name, svdir, sizeof svdef->name); tain_now(&svdef->when); svdef->bitflags |= SVDEF_FLAG_ACTIVE; /* open an fd to use for fchdir() in perpd_svrun(): */ cstr_vcopy(path_buf, "./", svdir); if((fd = open(path_buf, O_RDONLY)) == -1){ warn_syserr("failure open() on service definition directory ", svdir); return -1; } fd_cloexec(fd); svdef->fd_dir = fd; /* inspect service definition directory: */ cstr_vcopy(path_buf, "./", svdir, "/flag.down"); if(stat(path_buf, &st) != -1){ svdef->bitflags |= SVDEF_FLAG_DOWN; } cstr_vcopy(path_buf, "./", svdir, "/flag.once"); if(stat(path_buf, &st) != -1){ svdef->bitflags |= SVDEF_FLAG_ONCE; } /* logging? */ cstr_vcopy(path_buf, "./", svdir, "/rc.log"); if(stat(path_buf, &st) != -1){ if(st.st_mode & S_IXUSR){ svdef->bitflags |= SVDEF_FLAG_HASLOG; log_debug("rc.log exists and is executable for ", svdir); }else{ log_warning("rc.log exists but is not set executable for ", svdir); } } /* setup logpipe: */ if(svdef->bitflags & SVDEF_FLAG_HASLOG){ if(pipe(svdef->logpipe) == -1){ warn_syserr("failure pipe() on logpipe for ", svdir); close(fd); return -1; } fd_cloexec(svdef->logpipe[0]); fd_cloexec(svdef->logpipe[1]); } /* ** from here on, the service is considered activated */ /* first time startup: */ /* log: if FLAG_HASLOG, start irrespective of any other svdef->bitflags: */ if(svdef->bitflags & SVDEF_FLAG_HASLOG){ svdef->svpair[SUBSV_LOG].bitflags |= SUBSV_FLAG_ISLOG; perpd_svdef_run(svdef, SUBSV_LOG, SVRUN_START); } /* XXX, bail here if log startup fails on fork() ? */ /* main: */ if(!(svdef->bitflags & SVDEF_FLAG_DOWN)){ /* setup for running once? */ if(svdef->bitflags & SVDEF_FLAG_ONCE){ svdef->svpair[SUBSV_MAIN].bitflags |= SUBSV_FLAG_ISONCE; } perpd_svdef_run(svdef, SUBSV_MAIN, SVRUN_START); } else { svdef->svpair[SUBSV_MAIN].bitflags |= SUBSV_FLAG_WANTDOWN; } return 0; }
int main (int argc, char **argv) { #ifdef HAVE_LUA /* Lua runtime */ lua_State *L = NULL; #endif /* master file descriptor list */ fd_set used_fds, serv_fds, read_fds; /* working structs */ struct addrinfo hints; struct http_request *req; struct path_info *pin; struct client *cl; struct sigaction sa; struct config conf; /* signal mask */ sigset_t ss; /* maximum file descriptor number */ int new_fd, cur_fd, max_fd = 0; int tls = 0; int keys = 0; int bound = 0; int nofork = 0; /* args */ int opt; char bind[128]; char *port = NULL; /* library handles */ void *tls_lib; void *lua_lib; /* clear the master and temp sets */ FD_ZERO(&used_fds); FD_ZERO(&serv_fds); FD_ZERO(&read_fds); /* handle SIGPIPE, SIGINT, SIGTERM, SIGCHLD */ sa.sa_flags = 0; sigemptyset(&sa.sa_mask); sa.sa_handler = SIG_IGN; sigaction(SIGPIPE, &sa, NULL); sa.sa_handler = uh_sigchld; sigaction(SIGCHLD, &sa, NULL); sa.sa_handler = uh_sigterm; sigaction(SIGINT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); /* defer SIGCHLD */ sigemptyset(&ss); sigaddset(&ss, SIGCHLD); sigprocmask(SIG_BLOCK, &ss, NULL); /* prepare addrinfo hints */ memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; /* parse args */ memset(&conf, 0, sizeof(conf)); memset(bind, 0, sizeof(bind)); #ifdef HAVE_TLS /* load TLS plugin */ if( ! (tls_lib = dlopen("uhttpd_tls.so", RTLD_LAZY | RTLD_GLOBAL)) ) { fprintf(stderr, "Notice: Unable to load TLS plugin - disabling SSL support! " "(Reason: %s)\n", dlerror() ); } else { /* resolve functions */ if( !(conf.tls_init = dlsym(tls_lib, "uh_tls_ctx_init")) || !(conf.tls_cert = dlsym(tls_lib, "uh_tls_ctx_cert")) || !(conf.tls_key = dlsym(tls_lib, "uh_tls_ctx_key")) || !(conf.tls_free = dlsym(tls_lib, "uh_tls_ctx_free")) || !(conf.tls_accept = dlsym(tls_lib, "uh_tls_client_accept")) || !(conf.tls_close = dlsym(tls_lib, "uh_tls_client_close")) || !(conf.tls_recv = dlsym(tls_lib, "uh_tls_client_recv")) || !(conf.tls_send = dlsym(tls_lib, "uh_tls_client_send")) ) { fprintf(stderr, "Error: Failed to lookup required symbols " "in TLS plugin: %s\n", dlerror() ); exit(1); } /* init SSL context */ if( ! (conf.tls = conf.tls_init()) ) { fprintf(stderr, "Error: Failed to initalize SSL context\n"); exit(1); } } #endif while( (opt = getopt(argc, argv, "fC:K:p:s:h:c:l:L:d:r:m:x:t:")) > 0 ) { switch(opt) { /* [addr:]port */ case 'p': case 's': if( (port = strrchr(optarg, ':')) != NULL ) { if( (optarg[0] == '[') && (port > optarg) && (port[-1] == ']') ) memcpy(bind, optarg + 1, min(sizeof(bind), (int)(port - optarg) - 2)); else memcpy(bind, optarg, min(sizeof(bind), (int)(port - optarg))); port++; } else { port = optarg; } #ifdef HAVE_TLS if( opt == 's' ) { if( !conf.tls ) { fprintf(stderr, "Notice: TLS support is disabled, " "ignoring '-s %s'\n", optarg ); continue; } tls = 1; } #endif /* bind sockets */ bound += uh_socket_bind( &serv_fds, &max_fd, bind[0] ? bind : NULL, port, &hints, (opt == 's'), &conf ); break; #ifdef HAVE_TLS /* certificate */ case 'C': if( conf.tls ) { if( conf.tls_cert(conf.tls, optarg) < 1 ) { fprintf(stderr, "Error: Invalid certificate file given\n"); exit(1); } keys++; } break; /* key */ case 'K': if( conf.tls ) { if( conf.tls_key(conf.tls, optarg) < 1 ) { fprintf(stderr, "Error: Invalid private key file given\n"); exit(1); } keys++; } break; #endif /* docroot */ case 'h': if( ! realpath(optarg, conf.docroot) ) { fprintf(stderr, "Error: Invalid directory %s: %s\n", optarg, strerror(errno)); exit(1); } break; #ifdef HAVE_CGI /* cgi prefix */ case 'x': conf.cgi_prefix = optarg; break; #endif #ifdef HAVE_LUA /* lua prefix */ case 'l': conf.lua_prefix = optarg; break; /* lua handler */ case 'L': conf.lua_handler = optarg; break; #endif #if defined(HAVE_CGI) || defined(HAVE_LUA) /* script timeout */ case 't': conf.script_timeout = atoi(optarg); break; #endif /* no fork */ case 'f': nofork = 1; break; /* urldecode */ case 'd': if( (port = malloc(strlen(optarg)+1)) != NULL ) { memset(port, 0, strlen(optarg)+1); uh_urldecode(port, strlen(optarg), optarg, strlen(optarg)); printf("%s", port); free(port); exit(0); } break; /* basic auth realm */ case 'r': conf.realm = optarg; break; /* md5 crypt */ case 'm': printf("%s\n", crypt(optarg, "$1$")); exit(0); break; /* config file */ case 'c': conf.file = optarg; break; default: fprintf(stderr, "Usage: %s -p [addr:]port [-h docroot]\n" " -f Do not fork to background\n" " -c file Configuration file, default is '/etc/httpd.conf'\n" " -p [addr:]port Bind to specified address and port, multiple allowed\n" #ifdef HAVE_TLS " -s [addr:]port Like -p but provide HTTPS on this port\n" " -C file ASN.1 server certificate file\n" " -K file ASN.1 server private key file\n" #endif " -h directory Specify the document root, default is '.'\n" #ifdef HAVE_LUA " -l string URL prefix for Lua handler, default is '/lua'\n" " -L file Lua handler script, omit to disable Lua\n" #endif #ifdef HAVE_CGI " -x string URL prefix for CGI handler, default is '/cgi-bin'\n" #endif #if defined(HAVE_CGI) || defined(HAVE_LUA) " -t seconds CGI and Lua script timeout in seconds, default is 60\n" #endif " -d string URL decode given string\n" " -r string Specify basic auth realm\n" " -m string MD5 crypt given string\n" "\n", argv[0] ); exit(1); } } #ifdef HAVE_TLS if( (tls == 1) && (keys < 2) ) { fprintf(stderr, "Error: Missing private key or certificate file\n"); exit(1); } #endif if( bound < 1 ) { fprintf(stderr, "Error: No sockets bound, unable to continue\n"); exit(1); } /* default docroot */ if( !conf.docroot[0] && !realpath(".", conf.docroot) ) { fprintf(stderr, "Error: Can not determine default document root: %s\n", strerror(errno)); exit(1); } /* default realm */ if( ! conf.realm ) conf.realm = "Protected Area"; /* config file */ uh_config_parse(conf.file); #if defined(HAVE_CGI) || defined(HAVE_LUA) /* default script timeout */ if( conf.script_timeout <= 0 ) conf.script_timeout = 60; #endif #ifdef HAVE_CGI /* default cgi prefix */ if( ! conf.cgi_prefix ) conf.cgi_prefix = "/cgi-bin"; #endif #ifdef HAVE_LUA /* load Lua plugin */ if( ! (lua_lib = dlopen("uhttpd_lua.so", RTLD_LAZY | RTLD_GLOBAL)) ) { fprintf(stderr, "Notice: Unable to load Lua plugin - disabling Lua support! " "(Reason: %s)\n", dlerror() ); } else { /* resolve functions */ if( !(conf.lua_init = dlsym(lua_lib, "uh_lua_init")) || !(conf.lua_close = dlsym(lua_lib, "uh_lua_close")) || !(conf.lua_request = dlsym(lua_lib, "uh_lua_request")) ) { fprintf(stderr, "Error: Failed to lookup required symbols " "in Lua plugin: %s\n", dlerror() ); exit(1); } /* init Lua runtime if handler is specified */ if( conf.lua_handler ) { /* default lua prefix */ if( ! conf.lua_prefix ) conf.lua_prefix = "/lua"; L = conf.lua_init(conf.lua_handler); } } #endif /* fork (if not disabled) */ if( ! nofork ) { switch( fork() ) { case -1: perror("fork()"); exit(1); case 0: /* daemon setup */ if( chdir("/") ) perror("chdir()"); if( (cur_fd = open("/dev/null", O_WRONLY)) > -1 ) dup2(cur_fd, 0); if( (cur_fd = open("/dev/null", O_RDONLY)) > -1 ) dup2(cur_fd, 1); if( (cur_fd = open("/dev/null", O_RDONLY)) > -1 ) dup2(cur_fd, 2); break; default: exit(0); } } /* backup server descriptor set */ used_fds = serv_fds; /* loop */ while(run) { /* create a working copy of the used fd set */ read_fds = used_fds; /* sleep until socket activity */ if( select(max_fd + 1, &read_fds, NULL, NULL, NULL) == -1 ) { perror("select()"); exit(1); } /* run through the existing connections looking for data to be read */ for( cur_fd = 0; cur_fd <= max_fd; cur_fd++ ) { /* is a socket managed by us */ if( FD_ISSET(cur_fd, &read_fds) ) { /* is one of our listen sockets */ if( FD_ISSET(cur_fd, &serv_fds) ) { /* handle new connections */ if( (new_fd = accept(cur_fd, NULL, 0)) != -1 ) { /* add to global client list */ if( (cl = uh_client_add(new_fd, uh_listener_lookup(cur_fd))) != NULL ) { #ifdef HAVE_TLS /* setup client tls context */ if( conf.tls ) conf.tls_accept(cl); #endif /* add client socket to global fdset */ FD_SET(new_fd, &used_fds); fd_cloexec(new_fd); max_fd = max(max_fd, new_fd); } /* insufficient resources */ else { fprintf(stderr, "uh_client_add(): Can not manage more than " "%i client sockets, connection dropped\n", UH_LIMIT_CLIENTS ); close(new_fd); } } } /* is a client socket */ else { if( ! (cl = uh_client_lookup(cur_fd)) ) { /* this should not happen! */ fprintf(stderr, "uh_client_lookup(): No entry for fd %i!\n", cur_fd); goto cleanup; } /* parse message header */ if( (req = uh_http_header_recv(cl)) != NULL ) { #ifdef HAVE_LUA /* Lua request? */ if( L && uh_path_match(conf.lua_prefix, req->url) ) { conf.lua_request(cl, req, L); } else #endif /* dispatch request */ if( (pin = uh_path_lookup(cl, req->url)) != NULL ) { /* auth ok? */ if( uh_auth_check(cl, req, pin) ) { #ifdef HAVE_CGI if( uh_path_match(conf.cgi_prefix, pin->name) ) { uh_cgi_request(cl, req, pin); } else #endif { uh_file_request(cl, req, pin); } } } /* 404 */ else { uh_http_sendhf(cl, 404, "Not Found", "No such file or directory"); } } /* 400 */ else { uh_http_sendhf(cl, 400, "Bad Request", "Malformed request received"); } #ifdef HAVE_TLS /* free client tls context */ if( conf.tls ) conf.tls_close(cl); #endif cleanup: /* close client socket */ close(cur_fd); FD_CLR(cur_fd, &used_fds); /* remove from global client list */ uh_client_remove(cur_fd); } } } } #ifdef HAVE_LUA /* destroy the Lua state */ if( L != NULL ) conf.lua_close(L); #endif return 0; }
static int uh_socket_bind( fd_set *serv_fds, int *max_fd, const char *host, const char *port, struct addrinfo *hints, int do_tls, struct config *conf ) { int sock = -1; int yes = 1; int status; int bound = 0; struct listener *l = NULL; struct addrinfo *addrs = NULL, *p = NULL; if( (status = getaddrinfo(host, port, hints, &addrs)) != 0 ) { fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(status)); } /* try to bind a new socket to each found address */ for( p = addrs; p; p = p->ai_next ) { /* get the socket */ if( (sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1 ) { perror("socket()"); goto error; } /* "address already in use" */ if( setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1 ) { perror("setsockopt()"); goto error; } /* required to get parallel v4 + v6 working */ if( p->ai_family == AF_INET6 ) { if( setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)) == -1 ) { perror("setsockopt()"); goto error; } } /* bind */ if( bind(sock, p->ai_addr, p->ai_addrlen) == -1 ) { perror("bind()"); goto error; } /* listen */ if( listen(sock, UH_LIMIT_CLIENTS) == -1 ) { perror("listen()"); goto error; } /* add listener to global list */ if( ! (l = uh_listener_add(sock, conf)) ) { fprintf(stderr, "uh_listener_add(): Can not create more than " "%i listen sockets\n", UH_LIMIT_LISTENERS ); goto error; } #ifdef HAVE_TLS /* init TLS */ l->tls = do_tls ? conf->tls : NULL; #endif /* add socket to server fd set */ FD_SET(sock, serv_fds); fd_cloexec(sock); *max_fd = max(*max_fd, sock); bound++; continue; error: if( sock > 0 ) close(sock); } freeaddrinfo(addrs); return bound; }
static int uh_socket_bind( fd_set *serv_fds, int *max_fd, const char *host, const char *port, struct addrinfo *hints, int do_tls, struct config *conf ) { int sock = -1; int yes = 1; int status; int bound = 0; int tcp_ka_idl, tcp_ka_int, tcp_ka_cnt; struct listener *l = NULL; struct addrinfo *addrs = NULL, *p = NULL; if( (status = getaddrinfo(host, port, hints, &addrs)) != 0 ) { fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(status)); } /* try to bind a new socket to each found address */ for( p = addrs; p; p = p->ai_next ) { /* get the socket */ if( (sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1 ) { perror("socket()"); goto error; } /* "address already in use" */ if( setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) ) { perror("setsockopt()"); goto error; } /* TCP keep-alive */ if( conf->tcp_keepalive > 0 ) { tcp_ka_idl = 1; tcp_ka_cnt = 3; tcp_ka_int = conf->tcp_keepalive; if( setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(yes)) || setsockopt(sock, SOL_TCP, TCP_KEEPIDLE, &tcp_ka_idl, sizeof(tcp_ka_idl)) || setsockopt(sock, SOL_TCP, TCP_KEEPINTVL, &tcp_ka_int, sizeof(tcp_ka_int)) || setsockopt(sock, SOL_TCP, TCP_KEEPCNT, &tcp_ka_cnt, sizeof(tcp_ka_cnt)) ) { fprintf(stderr, "Notice: Unable to enable TCP keep-alive: %s\n", strerror(errno)); } } /* required to get parallel v4 + v6 working */ if( p->ai_family == AF_INET6 ) { if( setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)) == -1 ) { perror("setsockopt()"); goto error; } } /* bind */ if( bind(sock, p->ai_addr, p->ai_addrlen) == -1 ) { perror("bind()"); goto error; } /* listen */ if( listen(sock, UH_LIMIT_CLIENTS) == -1 ) { perror("listen()"); goto error; } /* add listener to global list */ if( ! (l = uh_listener_add(sock, conf)) ) { fprintf(stderr, "uh_listener_add(): Failed to allocate memory\n"); goto error; } #ifdef HAVE_TLS /* init TLS */ l->tls = do_tls ? conf->tls : NULL; #endif /* add socket to server fd set */ FD_SET(sock, serv_fds); fd_cloexec(sock); *max_fd = max(*max_fd, sock); bound++; continue; error: if( sock > 0 ) close(sock); } freeaddrinfo(addrs); return bound; }
int import_fork_tar_x(const char *path, pid_t *ret) { _cleanup_close_pair_ int pipefd[2] = { -1, -1 }; pid_t pid; int r; assert(path); assert(ret); if (pipe2(pipefd, O_CLOEXEC) < 0) return log_error_errno(errno, "Failed to create pipe for tar: %m"); pid = fork(); if (pid < 0) return log_error_errno(errno, "Failed to fork off tar: %m"); if (pid == 0) { int null_fd; uint64_t retain = (1ULL << CAP_CHOWN) | (1ULL << CAP_FOWNER) | (1ULL << CAP_FSETID) | (1ULL << CAP_MKNOD) | (1ULL << CAP_SETFCAP) | (1ULL << CAP_DAC_OVERRIDE); /* Child */ reset_all_signal_handlers(); reset_signal_mask(); assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); pipefd[1] = safe_close(pipefd[1]); if (dup2(pipefd[0], STDIN_FILENO) != STDIN_FILENO) { log_error_errno(errno, "Failed to dup2() fd: %m"); _exit(EXIT_FAILURE); } if (pipefd[0] != STDIN_FILENO) pipefd[0] = safe_close(pipefd[0]); null_fd = open("/dev/null", O_WRONLY|O_NOCTTY); if (null_fd < 0) { log_error_errno(errno, "Failed to open /dev/null: %m"); _exit(EXIT_FAILURE); } if (dup2(null_fd, STDOUT_FILENO) != STDOUT_FILENO) { log_error_errno(errno, "Failed to dup2() fd: %m"); _exit(EXIT_FAILURE); } if (null_fd != STDOUT_FILENO) null_fd = safe_close(null_fd); fd_cloexec(STDIN_FILENO, false); fd_cloexec(STDOUT_FILENO, false); fd_cloexec(STDERR_FILENO, false); if (unshare(CLONE_NEWNET) < 0) log_error_errno(errno, "Failed to lock tar into network namespace, ignoring: %m"); r = capability_bounding_set_drop(~retain, true); if (r < 0) log_error_errno(r, "Failed to drop capabilities, ignoring: %m"); execlp("tar", "tar", "--numeric-owner", "-C", path, "-px", NULL); log_error_errno(errno, "Failed to execute tar: %m"); _exit(EXIT_FAILURE); } pipefd[0] = safe_close(pipefd[0]); r = pipefd[1]; pipefd[1] = -1; *ret = pid; return r; }
static void uh_mainloop(struct config *conf, fd_set serv_fds, int max_fd) { /* master file descriptor list */ fd_set used_fds, read_fds; /* working structs */ struct http_request *req; struct path_info *pin; struct client *cl; /* maximum file descriptor number */ int new_fd, cur_fd = 0; /* clear the master and temp sets */ FD_ZERO(&used_fds); FD_ZERO(&read_fds); /* backup server descriptor set */ used_fds = serv_fds; /* loop */ while(run) { /* create a working copy of the used fd set */ read_fds = used_fds; /* sleep until socket activity */ if( select(max_fd + 1, &read_fds, NULL, NULL, NULL) == -1 ) { perror("select()"); exit(1); } /* run through the existing connections looking for data to be read */ for( cur_fd = 0; cur_fd <= max_fd; cur_fd++ ) { /* is a socket managed by us */ if( FD_ISSET(cur_fd, &read_fds) ) { /* is one of our listen sockets */ if( FD_ISSET(cur_fd, &serv_fds) ) { /* handle new connections */ if( (new_fd = accept(cur_fd, NULL, 0)) != -1 ) { /* add to global client list */ if( (cl = uh_client_add(new_fd, uh_listener_lookup(cur_fd))) != NULL ) { #ifdef HAVE_TLS /* setup client tls context */ if( conf->tls ) { if( conf->tls_accept(cl) < 1 ) { fprintf(stderr, "tls_accept failed, " "connection dropped\n"); /* close client socket */ close(new_fd); /* remove from global client list */ uh_client_remove(new_fd); continue; } } #endif /* add client socket to global fdset */ FD_SET(new_fd, &used_fds); fd_cloexec(new_fd); max_fd = max(max_fd, new_fd); } /* insufficient resources */ else { fprintf(stderr, "uh_client_add(): Cannot allocate memory\n"); close(new_fd); } } } /* is a client socket */ else { if( ! (cl = uh_client_lookup(cur_fd)) ) { /* this should not happen! */ fprintf(stderr, "uh_client_lookup(): No entry for fd %i!\n", cur_fd); goto cleanup; } /* parse message header */ if( (req = uh_http_header_recv(cl)) != NULL ) { /* RFC1918 filtering required? */ if( conf->rfc1918_filter && sa_rfc1918(&cl->peeraddr) && !sa_rfc1918(&cl->servaddr) ) { uh_http_sendhf(cl, 403, "Forbidden", "Rejected request from RFC1918 IP " "to public server address"); } else #ifdef HAVE_LUA /* Lua request? */ if( conf->lua_state && uh_path_match(conf->lua_prefix, req->url) ) { conf->lua_request(cl, req, conf->lua_state); } else #endif /* dispatch request */ if( (pin = uh_path_lookup(cl, req->url)) != NULL ) { /* auth ok? */ if( !pin->redirected && uh_auth_check(cl, req, pin) ) uh_dispatch_request(cl, req, pin); } /* 404 */ else { /* Try to invoke an error handler */ pin = uh_path_lookup(cl, conf->error_handler); if( pin && uh_auth_check(cl, req, pin) ) { req->redirect_status = 404; uh_dispatch_request(cl, req, pin); } else { uh_http_sendhf(cl, 404, "Not Found", "No such file or directory"); } } } #ifdef HAVE_TLS /* free client tls context */ if( conf->tls ) conf->tls_close(cl); #endif cleanup: /* close client socket */ close(cur_fd); FD_CLR(cur_fd, &used_fds); /* remove from global client list */ uh_client_remove(cur_fd); } } } } #ifdef HAVE_LUA /* destroy the Lua state */ if( conf->lua_state != NULL ) conf->lua_close(conf->lua_state); #endif }
int dmon_main (int argc, char **argv) { w_io_t *pidfile_io = NULL; char *opts_env = NULL; bool success; unsigned i, consumed; /* Check for "-C configfile" given in the command line. */ if (argc > 2 && ((argv[1][0] == '-' && argv[1][1] == 'C' && argv[1][2] == '\0') || !strcmp ("--config", argv[1]))) { w_lobj w_io_t *cfg_io = NULL; char *err_msg = NULL; if ((cfg_io = w_io_unix_open (argv[2], O_RDONLY, 0)) == NULL) w_die ("$s: Could not open file '$s', $E\n", argv[0], argv[2]); success = w_opt_parse_io (dmon_options, cfg_io, &err_msg); if (!success || err_msg) w_die ("$s: Error parsing '$s' at line $s\n", argv[0], argv[2], err_msg); replace_args_shift (2, &argc, &argv); } if ((opts_env = getenv ("DMON_OPTIONS")) != NULL) replace_args_string (opts_env, &argc, &argv); i = consumed = w_opt_parse (dmon_options, NULL, NULL, "cmd [cmd-options] [ -- " "log-cmd [log-cmd-options]]", argc, argv); W_DEBUG ("w_opt_parse consumed $I arguments\n", consumed); if (workdir_path) { if (chdir (workdir_path) != 0) w_die ("$s: Cannot use '$s' as work directory, $E\n", argv[0], workdir_path); } if (status_path) { status_io = w_io_unix_open (status_path, O_WRONLY | O_CREAT | O_APPEND, 0666); if (!status_io) w_die ("$s: Cannot open '$s' for writing, $E\n", argv[0], status_path); } if (cmd_interval && success_exit) w_die ("$s: Options '-i' and '-1' cannot be used together.\n", argv[0]); if (load_enabled && almost_zerof (load_low)) load_low = load_high / 2.0f; cmd_task.argv = argv + consumed; /* Skip over until "--" is found */ while (i < (unsigned) argc && strcmp (argv[i], "--") != 0) { cmd_task.argc++; i++; } /* There is a log command */ if (i < (unsigned) argc && strcmp (argv[i], "--") == 0) { log_task.argc = argc - cmd_task.argc - consumed - 1; log_task.argv = argv + argc - log_task.argc; log_task.argv[log_task.argc] = NULL; } cmd_task.argv[cmd_task.argc] = NULL; if (log_task.argc > 0) { if (pipe (log_fds) != 0) { w_die ("$s: Cannot create pipe: $E\n", argv[0]); } W_DEBUG ("pipe_read = $i, pipe_write = $i\n", log_fds[0], log_fds[1]); fd_cloexec (log_fds[0]); fd_cloexec (log_fds[1]); } #ifdef _DEBUG_PRINT { char **xxargv = cmd_task.argv; w_io_format (w_stderr, "cmd:"); while (*xxargv) w_io_format (w_stderr, " $s", *xxargv++); w_io_format (w_stderr, "\n"); if (log_enabled) { char **xxargv = log_task.argv; w_io_format (w_stderr, "log:"); while (*xxargv) w_io_format (w_stderr, " $c", *xxargv++); w_io_format (w_stderr, "\n"); } } #endif /* _DEBUG_PRINT */ if (cmd_task.argc == 0) w_die ("$s: No command to run given.\n", argv[0]); if (pidfile_path) { pidfile_io = w_io_unix_open (pidfile_path, O_TRUNC | O_CREAT | O_WRONLY, 0666); if (!pidfile_io) { w_die ("$s: cannot open '$s' for writing: $E\n", argv[0], pidfile_path); } } if (!nodaemon) become_daemon (); /* We have a valid file descriptor: write PID */ if (pidfile_io) { w_io_result_t r = w_io_format (pidfile_io, "$L\n", (unsigned long) getpid ()); if (w_io_failed (r)) W_WARN ("I/O error writing to PID file: $E\n"); w_obj_unref (pidfile_io); } setup_signals (); alarm (cmd_timeout); cmd_task.write_fd = log_fds[1]; log_task.read_fd = log_fds[0]; while (running) { W_DEBUG (">>> loop iteration\n"); if (check_child) { int retcode = reap_and_check (); /* * Wait the specified timeout but DO NOT use safe_sleep(): here * we want an interruptible sleep-wait so reaction to signals is * quick, which we definitely want for SIGINT/SIGTERM. */ if (cmd_interval && !success_exit && retcode == 0) { int retval; struct timespec ts; ts.tv_sec = cmd_interval; ts.tv_nsec = 0; do { retval = nanosleep (&ts, &ts); W_DEBUGC (" nanosleep -> $i\n", retval); } while (retval == -1 && errno == EINTR && running); } /* * Either handling signals which interrupt the previous loop, * or reap_and_check() may request stopping on successful exit */ if (!running) { task_action_queue (&cmd_task, A_NONE); break; } } task_action_dispatch_and_write_status ("cmd", &cmd_task); if (log_enabled) task_action_dispatch_and_write_status ("log", &log_task); if (load_enabled) { double load_cur; W_DEBUGC (" checking load after sleeping 1s\n"); interruptible_sleep (1); if (getloadavg (&load_cur, 1) == -1) W_WARN ("getloadavg() failed: $E\n"); if (paused) { /* If the current load dropped below load_low -> resume */ if (load_cur <= load_low) { W_DEBUGC (" resuming...\n"); task_signal (&cmd_task, SIGCONT); write_status ("cmd resume $L\n", (unsigned long) cmd_task.pid); paused = 0; } } else { /* If the load went above load_high -> pause */ if (load_cur > load_high) { W_DEBUGC (" pausing...\n"); task_signal (&cmd_task, SIGSTOP); write_status ("cmd pause $L\n", (unsigned long) cmd_task.pid); paused = 1; } } } else { /* Wait for signals to arrive. */ W_DEBUGC (" waiting for signals to come...\n"); pause (); } } W_DEBUG ("exiting gracefully...\n"); if (cmd_task.pid != NO_PID) { write_status ("cmd stop $L\n", (unsigned long) cmd_task.pid); task_action (&cmd_task, A_STOP); } if (log_enabled && log_task.pid != NO_PID) { write_status ("log stop $L\n", (unsigned long) log_task.pid); task_action (&log_task, A_STOP); } if (status_io) { w_obj_unref (status_io); status_io = NULL; } exit (EXIT_SUCCESS); }