int set_groups(AFPObj *obj, struct passwd *pwd) { if (initgroups(pwd->pw_name, pwd->pw_gid) < 0) LOG(log_error, logtype_afpd, "initgroups(%s, %d): %s", pwd->pw_name, pwd->pw_gid, strerror(errno)); if ((obj->ngroups = getgroups(0, NULL)) < 0) { LOG(log_error, logtype_afpd, "login: %s getgroups: %s", pwd->pw_name, strerror(errno)); return -1; } if (obj->groups) free(obj->groups); if (NULL == (obj->groups = calloc(obj->ngroups, sizeof(gid_t))) ) { LOG(log_error, logtype_afpd, "login: %s calloc: %d", obj->ngroups); return -1; } if ((obj->ngroups = getgroups(obj->ngroups, obj->groups)) < 0 ) { LOG(log_error, logtype_afpd, "login: %s getgroups: %s", pwd->pw_name, strerror(errno)); return -1; } return 0; }
int main (int argc, char **argv) { server *srv = NULL; int print_config = 0; int test_config = 0; int i_am_root; int o; int num_childs = 0; int pid_fd = -1, fd; size_t i; #ifdef HAVE_SIGACTION struct sigaction act; #endif #ifdef HAVE_GETRLIMIT struct rlimit rlim; #endif #ifdef USE_ALARM struct itimerval interval; interval.it_interval.tv_sec = 1; interval.it_interval.tv_usec = 0; interval.it_value.tv_sec = 1; interval.it_value.tv_usec = 0; #endif /* for nice %b handling in strfime() */ setlocale(LC_TIME, "C"); if (NULL == (srv = server_init())) { fprintf(stderr, "did this really happen?\n"); return -1; } /* init structs done */ srv->srvconf.port = 0; #ifdef HAVE_GETUID i_am_root = (getuid() == 0); #else i_am_root = 0; #endif srv->srvconf.dont_daemonize = 0; while(-1 != (o = getopt(argc, argv, "f:m:hvVDpt"))) { switch(o) { case 'f': if (srv->config_storage) { log_error_write(srv, __FILE__, __LINE__, "s", "Can only read one config file. Use the include command to use multiple config files."); server_free(srv); return -1; } if (config_read(srv, optarg)) { server_free(srv); return -1; } break; case 'm': buffer_copy_string(srv->srvconf.modules_dir, optarg); break; case 'p': print_config = 1; break; case 't': test_config = 1; break; case 'D': srv->srvconf.dont_daemonize = 1; break; case 'v': show_version(); return 0; case 'V': show_features(); return 0; case 'h': show_help(); return 0; default: show_help(); server_free(srv); return -1; } } if (!srv->config_storage) { log_error_write(srv, __FILE__, __LINE__, "s", "No configuration available. Try using -f option."); server_free(srv); return -1; } if (print_config) { data_unset *dc = srv->config_context->data[0]; if (dc) { dc->print(dc, 0); fprintf(stdout, "\n"); } else { /* shouldn't happend */ fprintf(stderr, "global config not found\n"); } } if (test_config) { printf("Syntax OK\n"); } if (test_config || print_config) { server_free(srv); return 0; } /* close stdin and stdout, as they are not needed */ openDevNull(STDIN_FILENO); openDevNull(STDOUT_FILENO); if (0 != config_set_defaults(srv)) { log_error_write(srv, __FILE__, __LINE__, "s", "setting default values failed"); server_free(srv); return -1; } /* UID handling */ #ifdef HAVE_GETUID if (!i_am_root && issetugid()) { /* we are setuid-root */ log_error_write(srv, __FILE__, __LINE__, "s", "Are you nuts ? Don't apply a SUID bit to this binary"); server_free(srv); return -1; } #endif /* check document-root */ if (srv->config_storage[0]->document_root->used <= 1) { log_error_write(srv, __FILE__, __LINE__, "s", "document-root is not set\n"); server_free(srv); return -1; } if (plugins_load(srv)) { log_error_write(srv, __FILE__, __LINE__, "s", "loading plugins finally failed"); plugins_free(srv); server_free(srv); return -1; } /* open pid file BEFORE chroot */ if (srv->srvconf.pid_file->used) { if (-1 == (pid_fd = open(srv->srvconf.pid_file->ptr, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))) { struct stat st; if (errno != EEXIST) { log_error_write(srv, __FILE__, __LINE__, "sbs", "opening pid-file failed:", srv->srvconf.pid_file, strerror(errno)); return -1; } if (0 != stat(srv->srvconf.pid_file->ptr, &st)) { log_error_write(srv, __FILE__, __LINE__, "sbs", "stating existing pid-file failed:", srv->srvconf.pid_file, strerror(errno)); } if (!S_ISREG(st.st_mode)) { log_error_write(srv, __FILE__, __LINE__, "sb", "pid-file exists and isn't regular file:", srv->srvconf.pid_file); return -1; } if (-1 == (pid_fd = open(srv->srvconf.pid_file->ptr, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))) { log_error_write(srv, __FILE__, __LINE__, "sbs", "opening pid-file failed:", srv->srvconf.pid_file, strerror(errno)); return -1; } } } if (srv->event_handler == FDEVENT_HANDLER_SELECT) { /* select limits itself * * as it is a hard limit and will lead to a segfault we add some safety * */ srv->max_fds = FD_SETSIZE - 200; } else { srv->max_fds = 4096; } if (i_am_root) { struct group *grp = NULL; struct passwd *pwd = NULL; int use_rlimit = 1; #ifdef HAVE_VALGRIND_VALGRIND_H if (RUNNING_ON_VALGRIND) use_rlimit = 0; #endif #ifdef HAVE_GETRLIMIT if (0 != getrlimit(RLIMIT_NOFILE, &rlim)) { log_error_write(srv, __FILE__, __LINE__, "ss", "couldn't get 'max filedescriptors'", strerror(errno)); return -1; } if (use_rlimit && srv->srvconf.max_fds) { /* set rlimits */ rlim.rlim_cur = srv->srvconf.max_fds; rlim.rlim_max = srv->srvconf.max_fds; if (0 != setrlimit(RLIMIT_NOFILE, &rlim)) { log_error_write(srv, __FILE__, __LINE__, "ss", "couldn't set 'max filedescriptors'", strerror(errno)); return -1; } } if (srv->event_handler == FDEVENT_HANDLER_SELECT) { srv->max_fds = rlim.rlim_cur < ((int)FD_SETSIZE) - 200 ? rlim.rlim_cur : FD_SETSIZE - 200; } else { srv->max_fds = rlim.rlim_cur; } /* set core file rlimit, if enable_cores is set */ if (use_rlimit && srv->srvconf.enable_cores && getrlimit(RLIMIT_CORE, &rlim) == 0) { rlim.rlim_cur = rlim.rlim_max; setrlimit(RLIMIT_CORE, &rlim); } #endif if (srv->event_handler == FDEVENT_HANDLER_SELECT) { /* don't raise the limit above FD_SET_SIZE */ if (srv->max_fds > ((int)FD_SETSIZE) - 200) { log_error_write(srv, __FILE__, __LINE__, "sd", "can't raise max filedescriptors above", FD_SETSIZE - 200, "if event-handler is 'select'. Use 'poll' or something else or reduce server.max-fds."); return -1; } } #ifdef HAVE_PWD_H /* set user and group */ if (srv->srvconf.username->used) { if (NULL == (pwd = getpwnam(srv->srvconf.username->ptr))) { log_error_write(srv, __FILE__, __LINE__, "sb", "can't find username", srv->srvconf.username); return -1; } if (pwd->pw_uid == 0) { log_error_write(srv, __FILE__, __LINE__, "s", "I will not set uid to 0\n"); return -1; } } if (srv->srvconf.groupname->used) { if (NULL == (grp = getgrnam(srv->srvconf.groupname->ptr))) { log_error_write(srv, __FILE__, __LINE__, "sb", "can't find groupname", srv->srvconf.groupname); return -1; } if (grp->gr_gid == 0) { log_error_write(srv, __FILE__, __LINE__, "s", "I will not set gid to 0\n"); return -1; } } #endif /* we need root-perms for port < 1024 */ if (0 != network_init(srv)) { plugins_free(srv); server_free(srv); return -1; } #ifdef HAVE_PWD_H /* * Change group before chroot, when we have access * to /etc/group * */ if (NULL != grp) { setgid(grp->gr_gid); setgroups(0, NULL); if (srv->srvconf.username->used) { initgroups(srv->srvconf.username->ptr, grp->gr_gid); } } #endif #ifdef HAVE_CHROOT if (srv->srvconf.changeroot->used) { tzset(); if (-1 == chroot(srv->srvconf.changeroot->ptr)) { log_error_write(srv, __FILE__, __LINE__, "ss", "chroot failed: ", strerror(errno)); return -1; } if (-1 == chdir("/")) { log_error_write(srv, __FILE__, __LINE__, "ss", "chdir failed: ", strerror(errno)); return -1; } } #endif #ifdef HAVE_PWD_H /* drop root privs */ if (NULL != pwd) { setuid(pwd->pw_uid); } #endif #if defined(HAVE_SYS_PRCTL_H) && defined(PR_SET_DUMPABLE) /** * on IRIX 6.5.30 they have prctl() but no DUMPABLE */ if (srv->srvconf.enable_cores) { prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); } #endif } else { #ifdef HAVE_GETRLIMIT if (0 != getrlimit(RLIMIT_NOFILE, &rlim)) { log_error_write(srv, __FILE__, __LINE__, "ss", "couldn't get 'max filedescriptors'", strerror(errno)); return -1; } /** * we are not root can can't increase the fd-limit, but we can reduce it */ if (srv->srvconf.max_fds && srv->srvconf.max_fds < rlim.rlim_cur) { /* set rlimits */ rlim.rlim_cur = srv->srvconf.max_fds; if (0 != setrlimit(RLIMIT_NOFILE, &rlim)) { log_error_write(srv, __FILE__, __LINE__, "ss", "couldn't set 'max filedescriptors'", strerror(errno)); return -1; } } if (srv->event_handler == FDEVENT_HANDLER_SELECT) { srv->max_fds = rlim.rlim_cur < ((int)FD_SETSIZE) - 200 ? rlim.rlim_cur : FD_SETSIZE - 200; } else { srv->max_fds = rlim.rlim_cur; } /* set core file rlimit, if enable_cores is set */ if (srv->srvconf.enable_cores && getrlimit(RLIMIT_CORE, &rlim) == 0) { rlim.rlim_cur = rlim.rlim_max; setrlimit(RLIMIT_CORE, &rlim); } #endif if (srv->event_handler == FDEVENT_HANDLER_SELECT) { /* don't raise the limit above FD_SET_SIZE */ if (srv->max_fds > ((int)FD_SETSIZE) - 200) { log_error_write(srv, __FILE__, __LINE__, "sd", "can't raise max filedescriptors above", FD_SETSIZE - 200, "if event-handler is 'select'. Use 'poll' or something else or reduce server.max-fds."); return -1; } } if (0 != network_init(srv)) { plugins_free(srv); server_free(srv); return -1; } } /* set max-conns */ if (srv->srvconf.max_conns > srv->max_fds/2) { /* we can't have more connections than max-fds/2 */ log_error_write(srv, __FILE__, __LINE__, "sdd", "can't have more connections than fds/2: ", srv->srvconf.max_conns, srv->max_fds); srv->max_conns = srv->max_fds/2; } else if (srv->srvconf.max_conns) { /* otherwise respect the wishes of the user */ srv->max_conns = srv->srvconf.max_conns; } else { /* or use the default: we really don't want to hit max-fds */ srv->max_conns = srv->max_fds/3; } if (HANDLER_GO_ON != plugins_call_init(srv)) { log_error_write(srv, __FILE__, __LINE__, "s", "Initialization of plugins failed. Going down."); plugins_free(srv); network_close(srv); server_free(srv); return -1; } #ifdef HAVE_FORK /* network is up, let's deamonize ourself */ if (srv->srvconf.dont_daemonize == 0) daemonize(); #endif srv->gid = getgid(); srv->uid = getuid(); /* write pid file */ if (pid_fd != -1) { buffer_copy_long(srv->tmp_buf, getpid()); buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("\n")); write(pid_fd, srv->tmp_buf->ptr, srv->tmp_buf->used - 1); close(pid_fd); pid_fd = -1; } /* Close stderr ASAP in the child process to make sure that nothing * is being written to that fd which may not be valid anymore. */ if (-1 == log_error_open(srv)) { log_error_write(srv, __FILE__, __LINE__, "s", "Opening errorlog failed. Going down."); plugins_free(srv); network_close(srv); server_free(srv); return -1; } if (HANDLER_GO_ON != plugins_call_set_defaults(srv)) { log_error_write(srv, __FILE__, __LINE__, "s", "Configuration of plugins failed. Going down."); plugins_free(srv); network_close(srv); server_free(srv); return -1; } /* dump unused config-keys */ for (i = 0; i < srv->config_context->used; i++) { array *config = ((data_config *)srv->config_context->data[i])->value; size_t j; for (j = 0; config && j < config->used; j++) { data_unset *du = config->data[j]; /* all var.* is known as user defined variable */ if (strncmp(du->key->ptr, "var.", sizeof("var.") - 1) == 0) { continue; } if (NULL == array_get_element(srv->config_touched, du->key->ptr)) { log_error_write(srv, __FILE__, __LINE__, "sbs", "WARNING: unknown config-key:", du->key, "(ignored)"); } } } if (srv->config_unsupported) { log_error_write(srv, __FILE__, __LINE__, "s", "Configuration contains unsupported keys. Going down."); } if (srv->config_deprecated) { log_error_write(srv, __FILE__, __LINE__, "s", "Configuration contains deprecated keys. Going down."); } if (srv->config_unsupported || srv->config_deprecated) { plugins_free(srv); network_close(srv); server_free(srv); return -1; } #ifdef HAVE_SIGACTION memset(&act, 0, sizeof(act)); act.sa_handler = SIG_IGN; sigaction(SIGPIPE, &act, NULL); sigaction(SIGUSR1, &act, NULL); # if defined(SA_SIGINFO) act.sa_sigaction = sigaction_handler; sigemptyset(&act.sa_mask); act.sa_flags = SA_SIGINFO; # else act.sa_handler = signal_handler; sigemptyset(&act.sa_mask); act.sa_flags = 0; # endif sigaction(SIGINT, &act, NULL); sigaction(SIGTERM, &act, NULL); sigaction(SIGHUP, &act, NULL); sigaction(SIGALRM, &act, NULL); sigaction(SIGCHLD, &act, NULL); #elif defined(HAVE_SIGNAL) /* ignore the SIGPIPE from sendfile() */ signal(SIGPIPE, SIG_IGN); signal(SIGUSR1, SIG_IGN); signal(SIGALRM, signal_handler); signal(SIGTERM, signal_handler); signal(SIGHUP, signal_handler); signal(SIGCHLD, signal_handler); signal(SIGINT, signal_handler); #endif #ifdef USE_ALARM signal(SIGALRM, signal_handler); /* setup periodic timer (1 second) */ if (setitimer(ITIMER_REAL, &interval, NULL)) { log_error_write(srv, __FILE__, __LINE__, "s", "setting timer failed"); return -1; } getitimer(ITIMER_REAL, &interval); #endif #ifdef HAVE_FORK /* start watcher and workers */ num_childs = srv->srvconf.max_worker; if (num_childs > 0) { int child = 0; while (!child && !srv_shutdown && !graceful_shutdown) { if (num_childs > 0) { switch (fork()) { case -1: return -1; case 0: child = 1; break; default: num_childs--; break; } } else { int status; if (-1 != wait(&status)) { /** * one of our workers went away */ num_childs++; } else { switch (errno) { case EINTR: /** * if we receive a SIGHUP we have to close our logs ourself as we don't * have the mainloop who can help us here */ if (handle_sig_hup) { handle_sig_hup = 0; log_error_cycle(srv); /** * forward to all procs in the process-group * * we also send it ourself */ if (!forwarded_sig_hup) { forwarded_sig_hup = 1; kill(0, SIGHUP); } } break; default: break; } } } } /** * for the parent this is the exit-point */ if (!child) { /** * kill all children too */ if (graceful_shutdown) { kill(0, SIGINT); } else if (srv_shutdown) { kill(0, SIGTERM); } log_error_close(srv); network_close(srv); connections_free(srv); plugins_free(srv); server_free(srv); return 0; } } #endif if (NULL == (srv->ev = fdevent_init(srv, srv->max_fds + 1, srv->event_handler))) { log_error_write(srv, __FILE__, __LINE__, "s", "fdevent_init failed"); return -1; } /* libev backend overwrites our SIGCHLD handler and calls waitpid on SIGCHLD; we want our own SIGCHLD handling. */ #ifdef HAVE_SIGACTION sigaction(SIGCHLD, &act, NULL); #elif defined(HAVE_SIGNAL) signal(SIGCHLD, signal_handler); #endif /* * kqueue() is called here, select resets its internals, * all server sockets get their handlers * * */ if (0 != network_register_fdevents(srv)) { plugins_free(srv); network_close(srv); server_free(srv); return -1; } /* might fail if user is using fam (not gamin) and famd isn't running */ if (NULL == (srv->stat_cache = stat_cache_init())) { log_error_write(srv, __FILE__, __LINE__, "s", "stat-cache could not be setup, dieing."); return -1; } #ifdef HAVE_FAM_H /* setup FAM */ if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM) { if (0 != FAMOpen2(srv->stat_cache->fam, "lighttpd")) { log_error_write(srv, __FILE__, __LINE__, "s", "could not open a fam connection, dieing."); return -1; } #ifdef HAVE_FAMNOEXISTS FAMNoExists(srv->stat_cache->fam); #endif srv->stat_cache->fam_fcce_ndx = -1; fdevent_register(srv->ev, FAMCONNECTION_GETFD(srv->stat_cache->fam), stat_cache_handle_fdevent, NULL); fdevent_event_set(srv->ev, &(srv->stat_cache->fam_fcce_ndx), FAMCONNECTION_GETFD(srv->stat_cache->fam), FDEVENT_IN); } #endif /* get the current number of FDs */ srv->cur_fds = open("/dev/null", O_RDONLY); close(srv->cur_fds); for (i = 0; i < srv->srv_sockets.used; i++) { server_socket *srv_socket = srv->srv_sockets.ptr[i]; if (-1 == fdevent_fcntl_set(srv->ev, srv_socket->fd)) { log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed:", strerror(errno)); return -1; } } /* main-loop */ while (!srv_shutdown) { int n; size_t ndx; time_t min_ts; if (handle_sig_hup) { handler_t r; /* reset notification */ handle_sig_hup = 0; /* cycle logfiles */ switch(r = plugins_call_handle_sighup(srv)) { case HANDLER_GO_ON: break; default: log_error_write(srv, __FILE__, __LINE__, "sd", "sighup-handler return with an error", r); break; } if (-1 == log_error_cycle(srv)) { log_error_write(srv, __FILE__, __LINE__, "s", "cycling errorlog failed, dying"); return -1; } else { #ifdef HAVE_SIGACTION log_error_write(srv, __FILE__, __LINE__, "sdsd", "logfiles cycled UID =", last_sighup_info.si_uid, "PID =", last_sighup_info.si_pid); #else log_error_write(srv, __FILE__, __LINE__, "s", "logfiles cycled"); #endif } } if (handle_sig_alarm) { /* a new second */ #ifdef USE_ALARM /* reset notification */ handle_sig_alarm = 0; #endif /* get current time */ min_ts = time(NULL); if (min_ts != srv->cur_ts) { int cs = 0; connections *conns = srv->conns; handler_t r; switch(r = plugins_call_handle_trigger(srv)) { case HANDLER_GO_ON: break; case HANDLER_ERROR: log_error_write(srv, __FILE__, __LINE__, "s", "one of the triggers failed"); break; default: log_error_write(srv, __FILE__, __LINE__, "d", r); break; } /* trigger waitpid */ srv->cur_ts = min_ts; /* cleanup stat-cache */ stat_cache_trigger_cleanup(srv); /** * check all connections for timeouts * */ for (ndx = 0; ndx < conns->used; ndx++) { int changed = 0; connection *con; int t_diff; con = conns->ptr[ndx]; if (con->state == CON_STATE_READ || con->state == CON_STATE_READ_POST) { if (con->request_count == 1) { if (srv->cur_ts - con->read_idle_ts > con->conf.max_read_idle) { /* time - out */ #if 0 log_error_write(srv, __FILE__, __LINE__, "sd", "connection closed - read-timeout:", con->fd); #endif connection_set_state(srv, con, CON_STATE_ERROR); changed = 1; } } else { if (srv->cur_ts - con->read_idle_ts > con->keep_alive_idle) { /* time - out */ #if 0 log_error_write(srv, __FILE__, __LINE__, "sd", "connection closed - read-timeout:", con->fd); #endif connection_set_state(srv, con, CON_STATE_ERROR); changed = 1; } } } if ((con->state == CON_STATE_WRITE) && (con->write_request_ts != 0)) { #if 0 if (srv->cur_ts - con->write_request_ts > 60) { log_error_write(srv, __FILE__, __LINE__, "sdd", "connection closed - pre-write-request-timeout:", con->fd, srv->cur_ts - con->write_request_ts); } #endif if (srv->cur_ts - con->write_request_ts > con->conf.max_write_idle) { /* time - out */ if (con->conf.log_timeouts) { log_error_write(srv, __FILE__, __LINE__, "sbsosds", "NOTE: a request for", con->request.uri, "timed out after writing", con->bytes_written, "bytes. We waited", (int)con->conf.max_write_idle, "seconds. If this a problem increase server.max-write-idle"); } connection_set_state(srv, con, CON_STATE_ERROR); changed = 1; } } if (con->state == CON_STATE_CLOSE && (srv->cur_ts - con->close_timeout_ts > HTTP_LINGER_TIMEOUT)) { changed = 1; } /* we don't like div by zero */ if (0 == (t_diff = srv->cur_ts - con->connection_start)) t_diff = 1; if (con->traffic_limit_reached && (con->conf.kbytes_per_second == 0 || ((con->bytes_written / t_diff) < con->conf.kbytes_per_second * 1024))) { /* enable connection again */ con->traffic_limit_reached = 0; changed = 1; } if (changed) { connection_state_machine(srv, con); } con->bytes_written_cur_second = 0; *(con->conf.global_bytes_per_second_cnt_ptr) = 0; #if 0 if (cs == 0) { fprintf(stderr, "connection-state: "); cs = 1; } fprintf(stderr, "c[%d,%d]: %s ", con->fd, con->fcgi.fd, connection_get_state(con->state)); #endif } if (cs == 1) fprintf(stderr, "\n"); } } if (srv->sockets_disabled) { /* our server sockets are disabled, why ? */ if ((srv->cur_fds + srv->want_fds < srv->max_fds * 8 / 10) && /* we have enough unused fds */ (srv->conns->used <= srv->max_conns * 9 / 10) && (0 == graceful_shutdown)) { for (i = 0; i < srv->srv_sockets.used; i++) { server_socket *srv_socket = srv->srv_sockets.ptr[i]; fdevent_event_set(srv->ev, &(srv_socket->fde_ndx), srv_socket->fd, FDEVENT_IN); } log_error_write(srv, __FILE__, __LINE__, "s", "[note] sockets enabled again"); srv->sockets_disabled = 0; } } else { if ((srv->cur_fds + srv->want_fds > srv->max_fds * 9 / 10) || /* out of fds */ (srv->conns->used >= srv->max_conns) || /* out of connections */ (graceful_shutdown)) { /* graceful_shutdown */ /* disable server-fds */ for (i = 0; i < srv->srv_sockets.used; i++) { server_socket *srv_socket = srv->srv_sockets.ptr[i]; fdevent_event_del(srv->ev, &(srv_socket->fde_ndx), srv_socket->fd); if (graceful_shutdown) { /* we don't want this socket anymore, * * closing it right away will make it possible for * the next lighttpd to take over (graceful restart) * */ fdevent_unregister(srv->ev, srv_socket->fd); close(srv_socket->fd); srv_socket->fd = -1; /* network_close() will cleanup after us */ if (srv->srvconf.pid_file->used && srv->srvconf.changeroot->used == 0) { if (0 != unlink(srv->srvconf.pid_file->ptr)) { if (errno != EACCES && errno != EPERM) { log_error_write(srv, __FILE__, __LINE__, "sbds", "unlink failed for:", srv->srvconf.pid_file, errno, strerror(errno)); } } } } } if (graceful_shutdown) { log_error_write(srv, __FILE__, __LINE__, "s", "[note] graceful shutdown started"); } else if (srv->conns->used >= srv->max_conns) { log_error_write(srv, __FILE__, __LINE__, "s", "[note] sockets disabled, connection limit reached"); } else { log_error_write(srv, __FILE__, __LINE__, "s", "[note] sockets disabled, out-of-fds"); } srv->sockets_disabled = 1; } } if (graceful_shutdown && srv->conns->used == 0) { /* we are in graceful shutdown phase and all connections are closed * we are ready to terminate without harming anyone */ srv_shutdown = 1; } /* we still have some fds to share */ if (srv->want_fds) { /* check the fdwaitqueue for waiting fds */ int free_fds = srv->max_fds - srv->cur_fds - 16; connection *con; for (; free_fds > 0 && NULL != (con = fdwaitqueue_unshift(srv, srv->fdwaitqueue)); free_fds--) { connection_state_machine(srv, con); srv->want_fds--; } } if ((n = fdevent_poll(srv->ev, 1000)) > 0) { /* n is the number of events */ int revents; int fd_ndx; #if 0 if (n > 0) { log_error_write(srv, __FILE__, __LINE__, "sd", "polls:", n); } #endif fd_ndx = -1; do { fdevent_handler handler; void *context; handler_t r; fd_ndx = fdevent_event_next_fdndx (srv->ev, fd_ndx); if (-1 == fd_ndx) break; /* not all fdevent handlers know how many fds got an event */ revents = fdevent_event_get_revent (srv->ev, fd_ndx); fd = fdevent_event_get_fd (srv->ev, fd_ndx); handler = fdevent_get_handler(srv->ev, fd); context = fdevent_get_context(srv->ev, fd); /* connection_handle_fdevent needs a joblist_append */ #if 0 log_error_write(srv, __FILE__, __LINE__, "sdd", "event for", fd, revents); #endif switch (r = (*handler)(srv, context, revents)) { case HANDLER_FINISHED: case HANDLER_GO_ON: case HANDLER_WAIT_FOR_EVENT: case HANDLER_WAIT_FOR_FD: break; case HANDLER_ERROR: /* should never happen */ SEGFAULT(); break; default: log_error_write(srv, __FILE__, __LINE__, "d", r); break; } } while (--n > 0); } else if (n < 0 && errno != EINTR) { log_error_write(srv, __FILE__, __LINE__, "ss", "fdevent_poll failed:", strerror(errno)); } for (ndx = 0; ndx < srv->joblist->used; ndx++) { connection *con = srv->joblist->ptr[ndx]; handler_t r; connection_state_machine(srv, con); switch(r = plugins_call_handle_joblist(srv, con)) { case HANDLER_FINISHED: case HANDLER_GO_ON: break; default: log_error_write(srv, __FILE__, __LINE__, "d", r); break; } con->in_joblist = 0; } srv->joblist->used = 0; } if (srv->srvconf.pid_file->used && srv->srvconf.changeroot->used == 0 && 0 == graceful_shutdown) { if (0 != unlink(srv->srvconf.pid_file->ptr)) { if (errno != EACCES && errno != EPERM) { log_error_write(srv, __FILE__, __LINE__, "sbds", "unlink failed for:", srv->srvconf.pid_file, errno, strerror(errno)); } } } #ifdef HAVE_SIGACTION log_error_write(srv, __FILE__, __LINE__, "sdsd", "server stopped by UID =", last_sigterm_info.si_uid, "PID =", last_sigterm_info.si_pid); #else log_error_write(srv, __FILE__, __LINE__, "s", "server stopped"); #endif /* clean-up */ log_error_close(srv); network_close(srv); connections_free(srv); plugins_free(srv); server_free(srv); return 0; }
main(int argc, char *argv[]) #endif { char options[128] = "aAc:CdD::fhHI:l:L:m:M:n:p:P:qrsS:UvV-:Y:"; int arg, i, ret; int dont_fork = 0, do_help = 0; int log_set = 0; int agent_mode = -1; char *pid_file = NULL; char option_compatability[] = "-Le"; #ifndef WIN32 int prepared_sockets = 0; #endif #if HAVE_GETPID int fd; FILE *PID; #endif #ifndef WIN32 #ifndef NETSNMP_NO_SYSTEMD /* check if systemd has sockets for us and don't close them */ prepared_sockets = netsnmp_sd_listen_fds(0); #endif /* NETSNMP_NO_SYSTEMD */ /* * close all non-standard file descriptors we may have * inherited from the shell. */ if (!prepared_sockets) { for (i = getdtablesize() - 1; i > 2; --i) { (void) close(i); } } #endif /* #WIN32 */ /* * register signals ASAP to prevent default action (usually core) * for signals during startup... */ #ifdef SIGTERM DEBUGMSGTL(("signal", "registering SIGTERM signal handler\n")); signal(SIGTERM, SnmpdShutDown); #endif #ifdef SIGINT DEBUGMSGTL(("signal", "registering SIGINT signal handler\n")); signal(SIGINT, SnmpdShutDown); #endif #ifdef SIGHUP signal(SIGHUP, SIG_IGN); /* do not terminate on early SIGHUP */ #endif #ifdef SIGUSR1 DEBUGMSGTL(("signal", "registering SIGUSR1 signal handler\n")); signal(SIGUSR1, SnmpdDump); #endif #ifdef SIGPIPE DEBUGMSGTL(("signal", "registering SIGPIPE signal handler\n")); signal(SIGPIPE, SIG_IGN); /* 'Inline' failure of wayward readers */ #endif #ifdef SIGXFSZ signal(SIGXFSZ, SnmpdCatchRandomSignal); #endif #ifdef NETSNMP_NO_ROOT_ACCESS /* * Default to no. */ netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_NO_ROOT_ACCESS, 1); #endif /* * Default to NOT running an AgentX master. */ netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_AGENTX_MASTER, 0); netsnmp_ds_set_int(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_AGENTX_TIMEOUT, -1); netsnmp_ds_set_int(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_AGENTX_RETRIES, -1); netsnmp_ds_set_int(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_CACHE_TIMEOUT, 5); /* * Add some options if they are available. */ #if HAVE_UNISTD_H strcat(options, "g:u:"); #endif #if defined(USING_AGENTX_SUBAGENT_MODULE)|| defined(USING_AGENTX_MASTER_MODULE) strcat(options, "x:"); #endif #ifdef USING_AGENTX_SUBAGENT_MODULE strcat(options, "X"); #endif /* * This is incredibly ugly, but it's probably the simplest way * to handle the old '-L' option as well as the new '-Lx' style */ for (i=0; i<argc; i++) { if (!strcmp(argv[i], "-L")) argv[i] = option_compatability; } #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG #ifdef WIN32 snmp_log_syslogname(app_name_long); #else snmp_log_syslogname(app_name); #endif #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG */ netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_APPTYPE, app_name); /* * Now process options normally. */ while ((arg = getopt(argc, argv, options)) != EOF) { switch (arg) { case '-': if (strcasecmp(optarg, "help") == 0) { usage(argv[0]); } if (strcasecmp(optarg, "version") == 0) { version(); } handle_long_opt(optarg); break; case 'a': log_addresses++; break; case 'A': netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_APPEND_LOGFILES, 1); break; case 'c': if (optarg != NULL) { netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OPTIONALCONFIG, optarg); } else { usage(argv[0]); } break; case 'C': netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DONT_READ_CONFIGS, 1); break; case 'd': netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DUMP_PACKET, ++snmp_dump_packet); break; case 'D': debug_register_tokens(optarg); snmp_set_do_debugging(1); break; case 'f': dont_fork = 1; break; #if HAVE_UNISTD_H case 'g': if (optarg != NULL) { char *ecp; int gid; gid = strtoul(optarg, &ecp, 10); #if HAVE_GETGRNAM && HAVE_PWD_H if (*ecp) { struct group *info; info = getgrnam(optarg); gid = info ? info->gr_gid : -1; endgrent(); } #endif if (gid < 0) { fprintf(stderr, "Bad group id: %s\n", optarg); exit(1); } netsnmp_ds_set_int(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_GROUPID, gid); } else { usage(argv[0]); } break; #endif case 'h': usage(argv[0]); break; case 'H': do_help = 1; break; case 'I': if (optarg != NULL) { add_to_init_list(optarg); } else { usage(argv[0]); } break; #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_FILE case 'l': printf("Warning: -l option is deprecated, use -Lf <file> instead\n"); if (optarg != NULL) { if (strlen(optarg) > PATH_MAX) { fprintf(stderr, "%s: logfile path too long (limit %d chars)\n", argv[0], PATH_MAX); exit(1); } snmp_enable_filelog(optarg, netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_APPEND_LOGFILES)); log_set = 1; } else { usage(argv[0]); } break; #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_FILE */ case 'L': if (snmp_log_options( optarg, argc, argv ) < 0 ) { usage(argv[0]); } log_set = 1; break; case 'm': if (optarg != NULL) { setenv("MIBS", optarg, 1); } else { usage(argv[0]); } break; case 'M': if (optarg != NULL) { setenv("MIBDIRS", optarg, 1); } else { usage(argv[0]); } break; case 'n': if (optarg != NULL) { app_name = optarg; netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_APPTYPE, app_name); } else { usage(argv[0]); } break; case 'P': printf("Warning: -P option is deprecated, use -p instead\n"); case 'p': if (optarg != NULL) { pid_file = optarg; } else { usage(argv[0]); } break; case 'q': netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_QUICK_PRINT, 1); break; case 'r': netsnmp_ds_toggle_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_NO_ROOT_ACCESS); break; #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG case 's': printf("Warning: -s option is deprecated, use -Lsd instead\n"); snmp_enable_syslog(); log_set = 1; break; case 'S': printf("Warning: -S option is deprecated, use -Ls <facility> instead\n"); if (optarg != NULL) { switch (*optarg) { case 'd': case 'D': Facility = LOG_DAEMON; break; case 'i': case 'I': Facility = LOG_INFO; break; case '0': Facility = LOG_LOCAL0; break; case '1': Facility = LOG_LOCAL1; break; case '2': Facility = LOG_LOCAL2; break; case '3': Facility = LOG_LOCAL3; break; case '4': Facility = LOG_LOCAL4; break; case '5': Facility = LOG_LOCAL5; break; case '6': Facility = LOG_LOCAL6; break; case '7': Facility = LOG_LOCAL7; break; default: fprintf(stderr, "invalid syslog facility: -S%c\n",*optarg); usage(argv[0]); } snmp_enable_syslog_ident(snmp_log_syslogname(NULL), Facility); log_set = 1; } else { fprintf(stderr, "no syslog facility specified\n"); usage(argv[0]); } break; #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG */ case 'U': netsnmp_ds_toggle_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_LEAVE_PIDFILE); break; #if HAVE_UNISTD_H case 'u': if (optarg != NULL) { char *ecp; int uid; uid = strtoul(optarg, &ecp, 10); #if HAVE_GETPWNAM && HAVE_PWD_H if (*ecp) { struct passwd *info; info = getpwnam(optarg); uid = info ? info->pw_uid : -1; endpwent(); } #endif if (uid < 0) { fprintf(stderr, "Bad user id: %s\n", optarg); exit(1); } netsnmp_ds_set_int(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_USERID, uid); } else { usage(argv[0]); } break; #endif case 'v': version(); case 'V': netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_VERBOSE, 1); break; #if defined(USING_AGENTX_SUBAGENT_MODULE)|| defined(USING_AGENTX_MASTER_MODULE) case 'x': if (optarg != NULL) { netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_X_SOCKET, optarg); } else { usage(argv[0]); } netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_AGENTX_MASTER, 1); break; #endif case 'X': #if defined(USING_AGENTX_SUBAGENT_MODULE) agent_mode = SUB_AGENT; #else fprintf(stderr, "%s: Illegal argument -X:" "AgentX support not compiled in.\n", argv[0]); usage(argv[0]); exit(1); #endif break; case 'Y': netsnmp_config_remember(optarg); break; default: usage(argv[0]); break; } } if (do_help) { netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_NO_ROOT_ACCESS, 1); init_agent(app_name); /* register our .conf handlers */ init_mib_modules(); init_snmp(app_name); fprintf(stderr, "Configuration directives understood:\n"); read_config_print_usage(" "); exit(0); } if (optind < argc) { #ifndef NETSNMP_NO_LISTEN_SUPPORT /* * There are optional transport addresses on the command line. */ DEBUGMSGTL(("snmpd/main", "optind %d, argc %d\n", optind, argc)); for (i = optind; i < argc; i++) { char *c, *astring; if ((c = netsnmp_ds_get_string(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_PORTS))) { astring = (char*)malloc(strlen(c) + 2 + strlen(argv[i])); if (astring == NULL) { fprintf(stderr, "malloc failure processing argv[%d]\n", i); exit(1); } sprintf(astring, "%s,%s", c, argv[i]); netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_PORTS, astring); SNMP_FREE(astring); } else { netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_PORTS, argv[i]); } } DEBUGMSGTL(("snmpd/main", "port spec: %s\n", netsnmp_ds_get_string(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_PORTS))); #else /* NETSNMP_NO_LISTEN_SUPPORT */ fprintf(stderr, "You specified ports to open; this agent was built to only send notifications\n"); exit(1); #endif /* NETSNMP_NO_LISTEN_SUPPORT */ } #ifdef NETSNMP_LOGFILE #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_FILE if (0 == log_set) snmp_enable_filelog(NETSNMP_LOGFILE, netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_APPEND_LOGFILES)); #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_FILE */ #endif #ifdef USING_UTIL_FUNCS_RESTART_MODULE { /* * Initialize a argv set to the current for restarting the agent. */ char *cptr, **argvptr; argvrestartp = (char **)malloc((argc + 2) * sizeof(char *)); argvptr = argvrestartp; for (i = 0, ret = 1; i < argc; i++) { ret += strlen(argv[i]) + 1; } argvrestart = (char *) malloc(ret); argvrestartname = (char *) malloc(strlen(argv[0]) + 1); if (!argvrestartp || !argvrestart || !argvrestartname) { fprintf(stderr, "malloc failure processing argvrestart\n"); exit(1); } strcpy(argvrestartname, argv[0]); for (cptr = argvrestart, i = 0; i < argc; i++) { strcpy(cptr, argv[i]); *(argvptr++) = cptr; cptr += strlen(argv[i]) + 1; } } #endif /* USING_UTIL_FUNCS_RESTART_MODULE */ if (agent_mode == -1) { if (strstr(argv[0], "agentxd") != NULL) { netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE, SUB_AGENT); } else { netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE, MASTER_AGENT); } } else { netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE, agent_mode); } SOCK_STARTUP; if (init_agent(app_name) != 0) { snmp_log(LOG_ERR, "Agent initialization failed\n"); exit(1); } init_mib_modules(); /* * start library */ init_snmp(app_name); if ((ret = init_master_agent()) != 0) { /* * Some error opening one of the specified agent transports. */ snmp_log(LOG_ERR, "Server Exiting with code 1\n"); exit(1); } /* * Initialize the world. Detach from the shell. Create initial user. */ if(!dont_fork) { int quit = ! netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_QUIT_IMMEDIATELY); ret = netsnmp_daemonize(quit, #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_STDIO snmp_stderrlog_status() #else /* NETSNMP_FEATURE_REMOVE_LOGGING_STDIO */ 0 #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_STDIO */ ); /* * xxx-rks: do we care if fork fails? I think we should... */ if(ret != 0) { snmp_log(LOG_ERR, "Server Exiting with code 1\n"); exit(1); } } #if HAVE_GETPID if (pid_file != NULL) { /* * unlink the pid_file, if it exists, prior to open. Without * doing this the open will fail if the user specified pid_file * already exists. */ unlink(pid_file); fd = open(pid_file, O_CREAT | O_EXCL | O_WRONLY, 0600); if (fd == -1) { snmp_log_perror(pid_file); if (!netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_NO_ROOT_ACCESS)) { exit(1); } } else { if ((PID = fdopen(fd, "w")) == NULL) { snmp_log_perror(pid_file); exit(1); } else { fprintf(PID, "%d\n", (int) getpid()); fclose(PID); } #ifndef _MSC_VER /* The sequence open()/fdopen()/fclose()/close() makes MSVC crash, hence skip the close() call when using the MSVC runtime. */ close(fd); #endif } } #endif #if defined(HAVE_UNISTD_H) && (defined(HAVE_CHOWN) || defined(HAVE_SETGID) || defined(HAVE_SETUID)) { const char *persistent_dir; int uid, gid; persistent_dir = get_persistent_directory(); mkdirhier( persistent_dir, NETSNMP_AGENT_DIRECTORY_MODE, 0 ); uid = netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_USERID); gid = netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_GROUPID); #ifdef HAVE_CHOWN if ( uid != 0 || gid != 0 ) chown( persistent_dir, uid, gid ); #endif #ifdef HAVE_SETGID if ((gid = netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_GROUPID)) > 0) { DEBUGMSGTL(("snmpd/main", "Changing gid to %d.\n", gid)); if (setgid(gid) == -1 #ifdef HAVE_SETGROUPS || setgroups(1, (gid_t *)&gid) == -1 #endif ) { snmp_log_perror("setgid failed"); if (!netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_NO_ROOT_ACCESS)) { exit(1); } } } #endif #ifdef HAVE_SETUID if ((uid = netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_USERID)) > 0) { #if HAVE_GETPWNAM && HAVE_PWD_H && HAVE_INITGROUPS struct passwd *info; /* * Set supplementary groups before changing UID * (which probably involves giving up privileges) */ info = getpwuid(uid); if (info) { DEBUGMSGTL(("snmpd/main", "Supplementary groups for %s.\n", info->pw_name)); if (initgroups(info->pw_name, (gid != 0 ? (gid_t)gid : info->pw_gid)) == -1) { snmp_log_perror("initgroups failed"); if (!netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_NO_ROOT_ACCESS)) { exit(1); } } } endpwent(); #endif DEBUGMSGTL(("snmpd/main", "Changing uid to %d.\n", uid)); if (setuid(uid) == -1) { snmp_log_perror("setuid failed"); if (!netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_NO_ROOT_ACCESS)) { exit(1); } } } #endif } #endif /* * Store persistent data immediately in case we crash later. */ snmp_store(app_name); #ifdef SIGHUP DEBUGMSGTL(("signal", "registering SIGHUP signal handler\n")); signal(SIGHUP, SnmpdReconfig); #endif /* * Send coldstart trap if possible. */ send_easy_trap(0, 0); /* * We're up, log our version number. */ snmp_log(LOG_INFO, "NET-SNMP version %s\n", netsnmp_get_version()); #ifdef WIN32SERVICE agent_status = AGENT_RUNNING; #endif netsnmp_addrcache_initialise(); /* * Let systemd know we're up. */ #ifndef NETSNMP_NO_SYSTEMD netsnmp_sd_notify(1, "READY=1\n"); if (prepared_sockets) /* * Clear the environment variable, we already processed all the sockets * by now. */ netsnmp_sd_listen_fds(1); #endif /* * Forever monitor the dest_port for incoming PDUs. */ DEBUGMSGTL(("snmpd/main", "We're up. Starting to process data.\n")); if (!netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_QUIT_IMMEDIATELY)) receive(); DEBUGMSGTL(("snmpd/main", "sending shutdown trap\n")); SnmpTrapNodeDown(); DEBUGMSGTL(("snmpd/main", "Bye...\n")); snmp_shutdown(app_name); shutdown_master_agent(); shutdown_agent(); if (!netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_LEAVE_PIDFILE) && (pid_file != NULL)) { unlink(pid_file); } #ifdef WIN32SERVICE agent_status = AGENT_STOPPED; #endif #ifdef USING_UTIL_FUNCS_RESTART_MODULE SNMP_FREE(argvrestartname); SNMP_FREE(argvrestart); SNMP_FREE(argvrestartp); #endif /* USING_UTIL_FUNCS_RESTART_MODULE */ SOCK_CLEANUP; return 0; } /* End main() -- snmpd */
int main(int argc, char *argv[]) { int listenfd; int res = 0; int lfd; struct flock lock; char pids[10]; parse_opts(argc, argv); argc -= optind; argv += optind; if (!foreground) { verbose += LL_WARNING; log_enable_syslog(); } else { verbose += LL_NOTICE; } /* set log level to specified verbosity */ log_level = verbose; usbmuxd_log(LL_NOTICE, "usbmuxd v%s starting up", PACKAGE_VERSION); should_exit = 0; should_discover = 0; set_signal_handlers(); signal(SIGPIPE, SIG_IGN); res = lfd = open(lockfile, O_WRONLY|O_CREAT, 0644); if(res == -1) { usbmuxd_log(LL_FATAL, "Could not open lockfile"); goto terminate; } lock.l_type = F_WRLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 0; lock.l_pid = 0; fcntl(lfd, F_GETLK, &lock); close(lfd); if (lock.l_type != F_UNLCK) { if (opt_exit) { if (lock.l_pid && !kill(lock.l_pid, 0)) { usbmuxd_log(LL_NOTICE, "Sending signal %d to instance with pid %d", exit_signal, lock.l_pid); res = 0; if (kill(lock.l_pid, exit_signal) < 0) { usbmuxd_log(LL_FATAL, "Could not deliver signal %d to pid %d", exit_signal, lock.l_pid); res = -1; } goto terminate; } else { usbmuxd_log(LL_ERROR, "Could not determine pid of the other running instance!"); res = -1; goto terminate; } } else { if (!opt_disable_hotplug) { usbmuxd_log(LL_ERROR, "Another instance is already running (pid %d). exiting.", lock.l_pid); res = -1; } else { usbmuxd_log(LL_NOTICE, "Another instance is already running (pid %d). Telling it to check for devices.", lock.l_pid); if (lock.l_pid && !kill(lock.l_pid, 0)) { usbmuxd_log(LL_NOTICE, "Sending signal SIGUSR2 to instance with pid %d", lock.l_pid); res = 0; if (kill(lock.l_pid, SIGUSR2) < 0) { usbmuxd_log(LL_FATAL, "Could not deliver SIGUSR2 to pid %d", lock.l_pid); res = -1; } } else { usbmuxd_log(LL_ERROR, "Could not determine pid of the other running instance!"); res = -1; } } goto terminate; } } unlink(lockfile); if (opt_exit) { usbmuxd_log(LL_NOTICE, "No running instance found, none killed. Exiting."); goto terminate; } if (!foreground) { if ((res = daemonize()) < 0) { fprintf(stderr, "usbmuxd: FATAL: Could not daemonize!\n"); usbmuxd_log(LL_FATAL, "Could not daemonize!"); goto terminate; } } // now open the lockfile and place the lock res = lfd = open(lockfile, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644); if(res < 0) { usbmuxd_log(LL_FATAL, "Could not open lockfile"); goto terminate; } lock.l_type = F_WRLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 0; if ((res = fcntl(lfd, F_SETLK, &lock)) < 0) { usbmuxd_log(LL_FATAL, "Lockfile locking failed!"); goto terminate; } sprintf(pids, "%d", getpid()); if ((size_t)(res = write(lfd, pids, strlen(pids))) != strlen(pids)) { usbmuxd_log(LL_FATAL, "Could not write pidfile!"); if(res >= 0) res = -2; goto terminate; } // set number of file descriptors to higher value struct rlimit rlim; getrlimit(RLIMIT_NOFILE, &rlim); rlim.rlim_max = 65536; setrlimit(RLIMIT_NOFILE, (const struct rlimit*)&rlim); usbmuxd_log(LL_INFO, "Creating socket"); res = listenfd = create_socket(); if(listenfd < 0) goto terminate; #ifdef HAVE_LIBIMOBILEDEVICE const char* userprefdir = config_get_config_dir(); struct stat fst; memset(&fst, '\0', sizeof(struct stat)); if (stat(userprefdir, &fst) < 0) { if (mkdir(userprefdir, 0775) < 0) { usbmuxd_log(LL_FATAL, "Failed to create required directory '%s': %s", userprefdir, strerror(errno)); res = -1; goto terminate; } if (stat(userprefdir, &fst) < 0) { usbmuxd_log(LL_FATAL, "stat() failed after creating directory '%s': %s", userprefdir, strerror(errno)); res = -1; goto terminate; } } // make sure permission bits are set correctly if (fst.st_mode != 02775) { if (chmod(userprefdir, 02775) < 0) { usbmuxd_log(LL_WARNING, "chmod(%s, 02775) failed: %s", userprefdir, strerror(errno)); } } #endif // drop elevated privileges if (drop_privileges && (getuid() == 0 || geteuid() == 0)) { struct passwd *pw; if (!drop_user) { usbmuxd_log(LL_FATAL, "No user to drop privileges to?"); res = -1; goto terminate; } pw = getpwnam(drop_user); if (!pw) { usbmuxd_log(LL_FATAL, "Dropping privileges failed, check if user '%s' exists!", drop_user); res = -1; goto terminate; } if (pw->pw_uid == 0) { usbmuxd_log(LL_INFO, "Not dropping privileges to root"); } else { #ifdef HAVE_LIBIMOBILEDEVICE /* make sure the non-privileged user has proper access to the config directory */ if ((fst.st_uid != pw->pw_uid) || (fst.st_gid != pw->pw_gid)) { if (chown(userprefdir, pw->pw_uid, pw->pw_gid) < 0) { usbmuxd_log(LL_WARNING, "chown(%s, %d, %d) failed: %s", userprefdir, pw->pw_uid, pw->pw_gid, strerror(errno)); } } #endif if ((res = initgroups(drop_user, pw->pw_gid)) < 0) { usbmuxd_log(LL_FATAL, "Failed to drop privileges (cannot set supplementary groups)"); goto terminate; } if ((res = setgid(pw->pw_gid)) < 0) { usbmuxd_log(LL_FATAL, "Failed to drop privileges (cannot set group ID to %d)", pw->pw_gid); goto terminate; } if ((res = setuid(pw->pw_uid)) < 0) { usbmuxd_log(LL_FATAL, "Failed to drop privileges (cannot set user ID to %d)", pw->pw_uid); goto terminate; } // security check if (setuid(0) != -1) { usbmuxd_log(LL_FATAL, "Failed to drop privileges properly!"); res = -1; goto terminate; } if (getuid() != pw->pw_uid || getgid() != pw->pw_gid) { usbmuxd_log(LL_FATAL, "Failed to drop privileges properly!"); res = -1; goto terminate; } usbmuxd_log(LL_NOTICE, "Successfully dropped privileges to '%s'", drop_user); } } client_init(); device_init(); usbmuxd_log(LL_INFO, "Initializing USB"); if((res = usb_init()) < 0) goto terminate; usbmuxd_log(LL_INFO, "%d device%s detected", res, (res==1)?"":"s"); usbmuxd_log(LL_NOTICE, "Initialization complete"); if (report_to_parent) if((res = notify_parent(0)) < 0) goto terminate; if(opt_disable_hotplug) { usbmuxd_log(LL_NOTICE, "Automatic device discovery on hotplug disabled."); usb_autodiscover(0); // discovery to be triggered by new instance } if (opt_enable_exit) { usbmuxd_log(LL_NOTICE, "Enabled exit on SIGUSR1 if no devices are attached. Start a new instance with \"--exit\" to trigger."); } res = main_loop(listenfd); if(res < 0) usbmuxd_log(LL_FATAL, "main_loop failed"); usbmuxd_log(LL_NOTICE, "usbmuxd shutting down"); device_kill_connections(); usb_shutdown(); device_shutdown(); client_shutdown(); usbmuxd_log(LL_NOTICE, "Shutdown complete"); terminate: log_disable_syslog(); if (res < 0) res = -res; else res = 0; if (report_to_parent) notify_parent(res); return res; }
void __attribute__ ((constructor)) run () { char *dir = 0, *uid_str = 0, *gid_str = 0, *user_str = 0; int uid = 0, gid = 0; if (0 == real_open) { if (0 == (real_open = dlsym (RTLD_NEXT, "open"))) { fprintf (stderr, "libchroot: dlsym(open): %s.\n", dlerror ()); abort (); } if (0 == (real_open64 = dlsym (RTLD_NEXT, "open64"))) { fprintf (stderr, "libchroot: dlsym(open): %s.\n", dlerror ()); abort (); } if (-1 == (fdzero = real_open ("/dev/zero", O_RDWR, 0))) { perror ("open /dev/zero failed"); abort (); } if (-1 == (fdnull = real_open ("/dev/null", O_RDWR, 0))) { perror ("open /dev/null failed"); abort (); } } if (0 == (dir = getenv ("CHROOT"))) { fputs ("libchroot: You forgot to specify $CHROOT.", stderr); abort (); } if ((gid_str = getenv ("SUDO_GID"))) { gid = atoi (gid_str); if ((user_str = getenv ("SUDO_USER"))) { if (-1 == initgroups (user_str, gid)) { perror ("initgroups"); abort (); } } } if (chdir (dir) == -1) { perror ("chdir"); abort (); } if (chroot (dir) == -1) { perror ("libchroot: chroot"); abort (); } if (0 == (uid_str = getenv ("SUDO_UID"))) { return; } uid = atoi (uid_str); if (uid != 0 && gid != 0) { if (-1 == setresgid (gid, gid, gid)) { perror ("setresgid failed"); abort (); } if (-1 == setresuid (uid, uid, uid)) { perror ("setresuid failed"); abort (); } } }
/* This is a *much* faster way of getting the list of groups for a user without changing the current supplemenrary group list. The old method used getgrent() which could take 20 minutes on a really big network with hundeds of thousands of groups and users. The new method takes a couple of seconds. NOTE!! this function only works if it is called as root! */ static int getgrouplist_internals(const char *user, gid_t gid, gid_t *groups, int *grpcnt) { gid_t *gids_saved; int ret, ngrp_saved, num_gids; if (non_root_mode()) { *grpcnt = 0; return 0; } /* work out how many groups we need to save */ ngrp_saved = getgroups(0, NULL); if (ngrp_saved == -1) { /* this shouldn't happen */ return -1; } gids_saved = (gid_t *)malloc(sizeof(gid_t) * (ngrp_saved+1)); if (!gids_saved) { errno = ENOMEM; return -1; } ngrp_saved = getgroups(ngrp_saved, gids_saved); if (ngrp_saved == -1) { SAFE_FREE(gids_saved); /* very strange! */ return -1; } if (initgroups(user, gid) != 0) { DEBUG(0, ("getgrouplist_internals: initgroups() failed!\n")); SAFE_FREE(gids_saved); return -1; } /* this must be done to cope with systems that put the current egid in the return from getgroups() */ save_re_gid(); set_effective_gid(gid); setgid(gid); num_gids = getgroups(0, NULL); if (num_gids + 1 > *grpcnt) { *grpcnt = num_gids + 1; ret = -1; } else { ret = getgroups(*grpcnt - 1, &groups[1]); if (ret >= 0) { groups[0] = gid; *grpcnt = ret + 1; } } restore_re_gid(); if (sys_setgroups(ngrp_saved, gids_saved) != 0) { /* yikes! */ DEBUG(0,("ERROR: getgrouplist: failed to reset group list!\n")); smb_panic("getgrouplist: failed to reset group list!\n"); free(gids_saved); return -1; } /* this will remove any duplicates gids in the list and update the group counter */ remove_duplicate_gids( grpcnt, groups ); free(gids_saved); return ret; }
/* * stat() or lstat() a path as a particular user/group. */ static int _path_stat(const char *path, int link, uid_t uid, gid_t gid) { struct stat sb; gid_t orig_gidset[NGROUPS_MAX]; int ngroups, status, stat_status; struct passwd *p; uint32_t orig_cache_enabled; /* disable L1 cache to avoid notification deadlock */ orig_cache_enabled = gL1CacheEnabled; gL1CacheEnabled = 0; /* get my group list */ memset(orig_gidset, 0, sizeof(orig_gidset)); ngroups = getgroups(NGROUPS_MAX, orig_gidset); if (ngroups < 0) { return PATH_STAT_FAILED; } /* look up user name */ p = getpwuid(uid); if (p == NULL) { gL1CacheEnabled = orig_cache_enabled; return PATH_STAT_FAILED; } /* switch to user's grouplist */ status = initgroups(p->pw_name, gid); if (status < 0) { gL1CacheEnabled = orig_cache_enabled; return PATH_STAT_FAILED; } /* reset gL1CacheEnabled */ gL1CacheEnabled = orig_cache_enabled; /* set thread credentials */ pthread_setugid_np(uid, gid); /* stat the file */ stat_status = -1; if (link != 0) { stat_status = lstat(path, &sb); } else { stat_status = stat(path, &sb); } /* unset thread credentials */ pthread_setugid_np(KAUTH_UID_NONE, KAUTH_GID_NONE); /* restore original grouplist for UID 0 */ status = syscall(SYS_initgroups, ngroups, orig_gidset, 0); if (status < 0) { return PATH_STAT_FAILED; } /* return status */ if (stat_status == 0) { return PATH_STAT_OK; } if (errno == EACCES) { return PATH_STAT_ACCESS; } return PATH_STAT_FAILED; }
int ngx_cdecl main(int argc, char *const *argv) { ngx_int_t i; ngx_log_t *log; ngx_cycle_t *cycle, init_cycle; ngx_core_conf_t *ccf; #if !(NGX_WIN32) struct rlimit rlmt; #endif if (ngx_get_options(argc, argv) != NGX_OK) { return 1; } if (ngx_show_version) { ngx_log_stderr(0, "nginx version: " NGINX_VER); if (ngx_show_help) { ngx_log_stderr(0, "Usage: nginx [-?hvVt] [-s signal] [-c filename] " "[-p prefix] [-g directives]" CRLF CRLF "Options:" CRLF " -?,-h : this help" CRLF " -v : show version and exit" CRLF " -V : show version and configure options then exit" CRLF " -t : test configuration and exit" CRLF " -s signal : send signal to a master process: " "stop, quit, reopen, reload" CRLF #ifdef NGX_PREFIX " -p prefix : set prefix path (default: " NGX_PREFIX ")" CRLF #else " -p prefix : set prefix path (default: NONE)" CRLF #endif " -c filename : set configuration file (default: " NGX_CONF_PATH ")" CRLF " -g directives : set global directives out of configuration " "file" CRLF ); } if (ngx_show_configure) { #ifdef NGX_COMPILER ngx_log_stderr(0, "built by " NGX_COMPILER); #endif #if (NGX_SSL) #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME ngx_log_stderr(0, "TLS SNI support enabled"); #else ngx_log_stderr(0, "TLS SNI support disabled"); #endif #endif ngx_log_stderr(0, "configure arguments:" NGX_CONFIGURE); } if (!ngx_test_config) { return 0; } } #if (NGX_FREEBSD) ngx_debug_init(); #endif /* TODO */ ngx_max_sockets = -1; ngx_time_init(); #if (NGX_PCRE) ngx_regex_init(); #endif ngx_pid = ngx_getpid(); log = ngx_log_init(ngx_prefix); if (log == NULL) { return 1; } /* STUB */ #if (NGX_OPENSSL) ngx_ssl_init(log); #endif /* * init_cycle->log is required for signal handlers and * ngx_process_options() */ ngx_memzero(&init_cycle, sizeof(ngx_cycle_t)); init_cycle.log = log; ngx_cycle = &init_cycle; init_cycle.pool = ngx_create_pool(1024, log); if (init_cycle.pool == NULL) { return 1; } if (ngx_save_argv(&init_cycle, argc, argv) != NGX_OK) { return 1; } if (ngx_process_options(&init_cycle) != NGX_OK) { return 1; } if (ngx_os_init(log) != NGX_OK) { return 1; } /* * ngx_crc32_table_init() requires ngx_cacheline_size set in ngx_os_init() */ if (ngx_crc32_table_init() != NGX_OK) { return 1; } if (ngx_add_inherited_sockets(&init_cycle) != NGX_OK) { return 1; } ngx_max_module = 0; for (i = 0; ngx_modules[i]; i++) { ngx_modules[i]->index = ngx_max_module++; } cycle = ngx_init_cycle(&init_cycle); if (cycle == NULL) { if (ngx_test_config) { ngx_log_stderr(0, "configuration file %s test failed", init_cycle.conf_file.data); } return 1; } if (ngx_test_config) { ngx_log_stderr(0, "configuration file %s test is successful", cycle->conf_file.data); return 0; } if (ngx_signal) { return ngx_signal_process(cycle, ngx_signal); } ngx_os_status(cycle->log); ngx_cycle = cycle; ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); if (ccf->master && ngx_process == NGX_PROCESS_SINGLE) { ngx_process = NGX_PROCESS_MASTER; } #if !(NGX_WIN32) if (geteuid() == 0) { if (ccf->rlimit_nofile != NGX_CONF_UNSET) { rlmt.rlim_cur = (rlim_t) ccf->rlimit_nofile; rlmt.rlim_max = (rlim_t) ccf->rlimit_nofile; if (setrlimit(RLIMIT_NOFILE, &rlmt) == -1) { ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, "setrlimit(RLIMIT_NOFILE, %i) failed", ccf->rlimit_nofile); } } if (ccf->rlimit_core != NGX_CONF_UNSET_SIZE) { rlmt.rlim_cur = (rlim_t) ccf->rlimit_core; rlmt.rlim_max = (rlim_t) ccf->rlimit_core; if (setrlimit(RLIMIT_CORE, &rlmt) == -1) { ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, "setrlimit(RLIMIT_CORE, %i) failed", ccf->rlimit_core); } } #ifdef RLIMIT_SIGPENDING if (ccf->rlimit_sigpending != NGX_CONF_UNSET) { rlmt.rlim_cur = (rlim_t) ccf->rlimit_sigpending; rlmt.rlim_max = (rlim_t) ccf->rlimit_sigpending; if (setrlimit(RLIMIT_SIGPENDING, &rlmt) == -1) { ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, "setrlimit(RLIMIT_SIGPENDING, %i) failed", ccf->rlimit_sigpending); } } #endif if (setgid(ccf->group) == -1) { ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, "setgid(%d) failed", ccf->group); /* fatal */ exit(2); } if (initgroups(ccf->username, ccf->group) == -1) { ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, "initgroups(%s, %d) failed", ccf->username, ccf->group); } if (setuid(ccf->user) == -1) { ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, "setuid(%d) failed", ccf->user); /* fatal */ exit(2); } } if (ngx_init_signals(cycle->log) != NGX_OK) { return 1; } if (!ngx_inherited && ccf->daemon) { if (ngx_daemon(cycle->log) != NGX_OK) { return 1; } ngx_daemonized = 1; } #endif if (ngx_create_pidfile(&ccf->pid, cycle->log) != NGX_OK) { return 1; } if (cycle->log->file->fd != ngx_stderr) { if (ngx_set_stderr(cycle->log->file->fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, ngx_set_stderr_n " failed"); return 1; } } if (log->file->fd != ngx_stderr) { if (ngx_close_file(log->file->fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, ngx_close_file_n " built-in log failed"); } } ngx_use_stderr = 0; if (ngx_process == NGX_PROCESS_SINGLE) { ngx_single_process_cycle(cycle); } else { ngx_master_process_cycle(cycle); } return 0; }
static void run_job(cronjob_t *job) /* Execute a cron job. Register its pid in the job structure. If a job's * crontab has an owner then its output is mailed to that owner, otherwise * no special provisions are made, so the output will go where cron's output * goes. This keeps root's mailbox from filling up. */ { pid_t pid; int need_mailer; int mailfd[2], errfd[2]; struct passwd *pw; crontab_t *tab= job->tab; need_mailer= (tab->user != nil); if (job->atjob) { struct stat st; need_mailer= 1; if (rename(tab->file, tab->data) < 0) { if (errno == ENOENT) { /* Normal error, job deleted. */ need_reload= 1; } else { /* Bad error, halt processing AT jobs. */ log(LOG_CRIT, "Can't rename %s: %s\n", tab->file, strerror(errno)); tab_reschedule(job); } return; } /* Will need to determine the next AT job. */ need_reload= 1; if (stat(tab->data, &st) < 0) { log(LOG_ERR, "Can't stat %s: %s\n", tab->data, strerror(errno)); tab_reschedule(job); return; } if ((pw= getpwuid(st.st_uid)) == nil) { log(LOG_ERR, "Unknown owner for uid %lu of AT job %s\n", (unsigned long) st.st_uid, job->cmd); tab_reschedule(job); return; } } else { pw= nil; if (job->user != nil && (pw= getpwnam(job->user)) == nil) { log(LOG_ERR, "%s: Unknown user\n", job->user); tab_reschedule(job); return; } } if (need_mailer) { errfd[0]= -1; if (pipe(errfd) < 0 || pipe(mailfd) < 0) { log(LOG_ERR, "pipe() call failed: %s\n", strerror(errno)); if (errfd[0] != -1) { close(errfd[0]); close(errfd[1]); } tab_reschedule(job); return; } (void) fcntl(errfd[1], F_SETFD, fcntl(errfd[1], F_GETFD) | FD_CLOEXEC); if ((pid= fork()) == -1) { log(LOG_ERR, "fork() call failed: %s\n", strerror(errno)); close(errfd[0]); close(errfd[1]); close(mailfd[0]); close(mailfd[1]); tab_reschedule(job); return; } if (pid == 0) { /* Child that is to be the mailer. */ char subject[70+20], *ps; close(errfd[0]); close(mailfd[1]); if (mailfd[0] != 0) { dup2(mailfd[0], 0); close(mailfd[0]); } memset(subject, 0, sizeof(subject)); sprintf(subject, "Output from your %s job: %.50s", job->atjob ? "AT" : "cron", job->cmd); if (subject[70] != 0) { strcpy(subject+70-3, "..."); } for (ps= subject; *ps != 0; ps++) { if (*ps == '\n') *ps= '%'; } execl("/usr/bin/mail", "mail", "-s", subject, pw->pw_name, (char *) nil); write(errfd[1], &errno, sizeof(errno)); _exit(1); } close(mailfd[0]); close(errfd[1]); if (read(errfd[0], &errno, sizeof(errno)) > 0) { log(LOG_ERR, "can't execute /usr/bin/mail: %s\n", strerror(errno)); close(errfd[0]); close(mailfd[1]); tab_reschedule(job); return; } close(errfd[0]); } if (pipe(errfd) < 0) { log(LOG_ERR, "pipe() call failed: %s\n", strerror(errno)); if (need_mailer) close(mailfd[1]); tab_reschedule(job); return; } (void) fcntl(errfd[1], F_SETFD, fcntl(errfd[1], F_GETFD) | FD_CLOEXEC); if ((pid= fork()) == -1) { log(LOG_ERR, "fork() call failed: %s\n", strerror(errno)); close(errfd[0]); close(errfd[1]); if (need_mailer) close(mailfd[1]); tab_reschedule(job); return; } if (pid == 0) { /* Child that is to be the cron job. */ close(errfd[0]); if (need_mailer) { if (mailfd[1] != 1) { dup2(mailfd[1], 1); close(mailfd[1]); } dup2(1, 2); } if (pw != nil) { /* Change id to the owner of the job. */ initgroups(pw->pw_name, pw->pw_gid); setgid(pw->pw_gid); setuid(pw->pw_uid); chdir(pw->pw_dir); if (setenv("USER", pw->pw_name, 1) < 0) goto bad; if (setenv("LOGNAME", pw->pw_name, 1) < 0) goto bad; if (setenv("HOME", pw->pw_dir, 1) < 0) goto bad; if (setenv("SHELL", pw->pw_shell[0] == 0 ? "/bin/sh" : pw->pw_shell, 1) < 0) goto bad; } if (job->atjob) { execl("/bin/sh", "sh", tab->data, (char *) nil); } else { execl("/bin/sh", "sh", "-c", job->cmd, (char *) nil); } bad: write(errfd[1], &errno, sizeof(errno)); _exit(1); } if (need_mailer) close(mailfd[1]); close(errfd[1]); if (read(errfd[0], &errno, sizeof(errno)) > 0) { log(LOG_ERR, "can't execute /bin/sh: %s\n", strerror(errno)); close(errfd[0]); tab_reschedule(job); return; } close(errfd[0]); job->pid= pid; if (debug >= 1) fprintf(stderr, "executing >%s<, pid = %ld\n", job->cmd, (long) job->pid); }
static int drop_root(void) { struct passwd *pw; struct group * gr; int r; if (!(pw = getpwnam(AVAHI_USER))) { avahi_log_error( "Failed to find user '"AVAHI_USER"'."); return -1; } if (!(gr = getgrnam(AVAHI_GROUP))) { avahi_log_error( "Failed to find group '"AVAHI_GROUP"'."); return -1; } avahi_log_info("Found user '"AVAHI_USER"' (UID %lu) and group '"AVAHI_GROUP"' (GID %lu).", (unsigned long) pw->pw_uid, (unsigned long) gr->gr_gid); if (initgroups(AVAHI_USER, gr->gr_gid) != 0) { avahi_log_error("Failed to change group list: %s", strerror(errno)); return -1; } #if defined(HAVE_SETRESGID) r = setresgid(gr->gr_gid, gr->gr_gid, gr->gr_gid); #elif defined(HAVE_SETEGID) if ((r = setgid(gr->gr_gid)) >= 0) r = setegid(gr->gr_gid); #elif defined(HAVE_SETREGID) r = setregid(gr->gr_gid, gr->gr_gid); #else #error "No API to drop privileges" #endif if (r < 0) { avahi_log_error("Failed to change GID: %s", strerror(errno)); return -1; } #if defined(HAVE_SETRESUID) r = setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid); #elif defined(HAVE_SETEUID) if ((r = setuid(pw->pw_uid)) >= 0) r = seteuid(pw->pw_uid); #elif defined(HAVE_SETREUID) r = setreuid(pw->pw_uid, pw->pw_uid); #else #error "No API to drop privileges" #endif if (r < 0) { avahi_log_error("Failed to change UID: %s", strerror(errno)); return -1; } set_env("USER", pw->pw_name); set_env("LOGNAME", pw->pw_name); set_env("HOME", pw->pw_dir); avahi_log_info("Successfully dropped root privileges."); return 0; }
/* the main program... */ int main(int argc, char *argv[]) { int i; sigset_t signalmask, oldmask; #ifdef HAVE_PTHREAD_TIMEDJOIN_NP struct timespec ts; #endif /* HAVE_PTHREAD_TIMEDJOIN_NP */ /* close all file descriptors (except stdin/out/err) */ i = sysconf(_SC_OPEN_MAX) - 1; /* if the system does not have OPEN_MAX just close the first 32 and hope we closed enough */ if (i < 0) i = 32; for (; i > 3; i--) close(i); /* parse the command line */ parse_cmdline(argc, argv); /* clean the environment */ #ifdef HAVE_CLEARENV if (clearenv() || putenv("HOME=/") || putenv("TMPDIR=/tmp") || putenv("LDAPNOINIT=1")) { log_log(LOG_ERR, "clearing environment failed"); exit(EXIT_FAILURE); } #else /* not HAVE_CLEARENV */ /* this is a bit ugly */ environ = sane_environment; #endif /* not HAVE_CLEARENV */ /* disable the nss_ldap module for this process */ disable_nss_ldap(); /* set LDAP log level */ if (myldap_set_debuglevel(nslcd_debugging) != LDAP_SUCCESS) exit(EXIT_FAILURE); /* read configuration file */ cfg_init(NSLCD_CONF_PATH); /* set default mode for pidfile and socket */ (void)umask((mode_t)0022); /* see if someone already locked the pidfile if --check option was given exit TRUE if daemon runs (pidfile locked), FALSE otherwise */ if (nslcd_checkonly) { if (is_locked(NSLCD_PIDFILE)) { log_log(LOG_DEBUG, "pidfile (%s) is locked", NSLCD_PIDFILE); exit(EXIT_SUCCESS); } else { log_log(LOG_DEBUG, "pidfile (%s) is not locked", NSLCD_PIDFILE); exit(EXIT_FAILURE); } } /* normal check for pidfile locked */ if (is_locked(NSLCD_PIDFILE)) { log_log(LOG_ERR, "daemon may already be active, cannot acquire lock (%s): %s", NSLCD_PIDFILE, strerror(errno)); exit(EXIT_FAILURE); } /* daemonize */ if ((!nslcd_debugging) && (!nslcd_nofork) && (daemon(0, 0) < 0)) { log_log(LOG_ERR, "unable to daemonize: %s", strerror(errno)); exit(EXIT_FAILURE); } /* intilialize logging */ if (!nslcd_debugging) log_startlogging(); log_log(LOG_INFO, "version %s starting", VERSION); /* start subprocess to do invalidating if reconnect_invalidate is set */ for (i = 0; i < LM_NONE; i++) if (nslcd_cfg->reconnect_invalidate[i]) break; if (i < LM_NONE) invalidator_start(); /* write pidfile */ create_pidfile(NSLCD_PIDFILE); /* install handler to close stuff off on exit and log notice */ if (atexit(exithandler)) { log_log(LOG_ERR, "atexit() failed: %s", strerror(errno)); exit(EXIT_FAILURE); } /* create socket */ nslcd_serversocket = create_socket(NSLCD_SOCKET); if ((nslcd_cfg->gid != NOGID) && (nslcd_cfg->uidname != NULL)) { #ifdef HAVE_INITGROUPS /* load supplementary groups */ if (initgroups(nslcd_cfg->uidname, nslcd_cfg->gid) < 0) log_log(LOG_WARNING, "cannot initgroups(\"%s\",%d) (ignored): %s", nslcd_cfg->uidname, (int)nslcd_cfg->gid, strerror(errno)); else log_log(LOG_DEBUG, "initgroups(\"%s\",%d) done", nslcd_cfg->uidname, (int)nslcd_cfg->gid); #else /* not HAVE_INITGROUPS */ #ifdef HAVE_SETGROUPS /* just drop all supplemental groups */ if (setgroups(0, NULL) < 0) log_log(LOG_WARNING, "cannot setgroups(0,NULL) (ignored): %s", strerror(errno)); else log_log(LOG_DEBUG, "setgroups(0,NULL) done"); #else /* not HAVE_SETGROUPS */ log_log(LOG_DEBUG, "neither initgroups() or setgroups() available"); #endif /* not HAVE_SETGROUPS */ #endif /* not HAVE_INITGROUPS */ } /* change to nslcd gid */ if (nslcd_cfg->gid != NOGID) { if (setgid(nslcd_cfg->gid) != 0) { log_log(LOG_ERR, "cannot setgid(%d): %s", (int)nslcd_cfg->gid, strerror(errno)); exit(EXIT_FAILURE); } log_log(LOG_DEBUG, "setgid(%d) done", (int)nslcd_cfg->gid); } /* change to nslcd uid */ if (nslcd_cfg->uid != NOUID) { if (setuid(nslcd_cfg->uid) != 0) { log_log(LOG_ERR, "cannot setuid(%d): %s", (int)nslcd_cfg->uid, strerror(errno)); exit(EXIT_FAILURE); } log_log(LOG_DEBUG, "setuid(%d) done", (int)nslcd_cfg->uid); } /* block all these signals so our worker threads won't handle them */ sigemptyset(&signalmask); sigaddset(&signalmask, SIGHUP); sigaddset(&signalmask, SIGINT); sigaddset(&signalmask, SIGQUIT); sigaddset(&signalmask, SIGABRT); sigaddset(&signalmask, SIGPIPE); sigaddset(&signalmask, SIGTERM); sigaddset(&signalmask, SIGUSR1); sigaddset(&signalmask, SIGUSR2); pthread_sigmask(SIG_BLOCK, &signalmask, &oldmask); /* start worker threads */ log_log(LOG_INFO, "accepting connections"); nslcd_threads = (pthread_t *)malloc(nslcd_cfg->threads * sizeof(pthread_t)); if (nslcd_threads == NULL) { log_log(LOG_CRIT, "main(): malloc() failed to allocate memory"); exit(EXIT_FAILURE); } for (i = 0; i < nslcd_cfg->threads; i++) { if (pthread_create(&nslcd_threads[i], NULL, worker, NULL)) { log_log(LOG_ERR, "unable to start worker thread %d: %s", i, strerror(errno)); exit(EXIT_FAILURE); } } pthread_sigmask(SIG_SETMASK, &oldmask, NULL); /* install signalhandlers for some signals */ install_sighandler(SIGHUP, sig_handler); install_sighandler(SIGINT, sig_handler); install_sighandler(SIGQUIT, sig_handler); install_sighandler(SIGABRT, sig_handler); install_sighandler(SIGPIPE, SIG_IGN); install_sighandler(SIGTERM, sig_handler); install_sighandler(SIGUSR1, sig_handler); install_sighandler(SIGUSR2, SIG_IGN); /* wait until we received a signal */ while ((nslcd_receivedsignal == 0) || (nslcd_receivedsignal == SIGUSR1)) { sleep(INT_MAX); /* sleep as long as we can or until we receive a signal */ if (nslcd_receivedsignal == SIGUSR1) { log_log(LOG_INFO, "caught signal %s (%d), refresh retries", signame(nslcd_receivedsignal), nslcd_receivedsignal); myldap_immediate_reconnect(); nslcd_receivedsignal = 0; } } /* print something about received signal */ log_log(LOG_INFO, "caught signal %s (%d), shutting down", signame(nslcd_receivedsignal), nslcd_receivedsignal); /* cancel all running threads */ for (i = 0; i < nslcd_cfg->threads; i++) if (pthread_cancel(nslcd_threads[i])) log_log(LOG_WARNING, "failed to stop thread %d (ignored): %s", i, strerror(errno)); /* close server socket to trigger failures in threads waiting on accept() */ close(nslcd_serversocket); nslcd_serversocket = -1; /* if we can, wait a few seconds for the threads to finish */ #ifdef HAVE_PTHREAD_TIMEDJOIN_NP ts.tv_sec = time(NULL) + 3; ts.tv_nsec = 0; #endif /* HAVE_PTHREAD_TIMEDJOIN_NP */ for (i = 0; i < nslcd_cfg->threads; i++) { #ifdef HAVE_PTHREAD_TIMEDJOIN_NP pthread_timedjoin_np(nslcd_threads[i], NULL, &ts); #endif /* HAVE_PTHREAD_TIMEDJOIN_NP */ if (pthread_kill(nslcd_threads[i], 0) == 0) log_log(LOG_ERR, "thread %d is still running, shutting down anyway", i); } /* we're done */ return EXIT_FAILURE; }
int main(int argc, char **argv) { char *my_socket, *pt; const struct optstruct *opt; struct optstruct *opts; time_t currtime; mode_t umsk; int ret; cl_initialize_crypto(); memset(&descr, 0, sizeof(struct smfiDesc)); descr.xxfi_name = "ClamAV"; /* filter name */ descr.xxfi_version = SMFI_VERSION; /* milter version */ descr.xxfi_flags = SMFIF_QUARANTINE; /* flags */ descr.xxfi_connect = clamfi_connect; /* connection info filter */ descr.xxfi_envfrom = clamfi_envfrom; /* envelope sender filter */ descr.xxfi_envrcpt = clamfi_envrcpt; /* envelope recipient filter */ descr.xxfi_header = clamfi_header; /* header filter */ descr.xxfi_body = clamfi_body; /* body block */ descr.xxfi_eom = clamfi_eom; /* end of message */ descr.xxfi_abort = clamfi_abort; /* message aborted */ opts = optparse(NULL, argc, argv, 1, OPT_MILTER, 0, NULL); if (!opts) { mprintf("!Can't parse command line options\n"); return 1; } if(optget(opts, "help")->enabled) { printf("Usage: %s [-c <config-file>]\n\n", argv[0]); printf(" --help -h Show this help\n"); printf(" --version -V Show version and exit\n"); printf(" --config-file <file> -c Read configuration from file\n\n"); optfree(opts); return 0; } if(opts->filename) { int x; for(x = 0; opts->filename[x]; x++) mprintf("^Ignoring option %s\n", opts->filename[x]); } if(optget(opts, "version")->enabled) { printf("clamav-milter %s\n", get_version()); optfree(opts); return 0; } pt = strdup(optget(opts, "config-file")->strarg); if (pt == NULL) { printf("Unable to allocate memory for config file\n"); return 1; } if((opts = optparse(pt, 0, NULL, 1, OPT_MILTER, 0, opts)) == NULL) { printf("%s: cannot parse config file %s\n", argv[0], pt); free(pt); return 1; } free(pt); if((opt = optget(opts, "Chroot"))->enabled) { if(chdir(opt->strarg) != 0) { logg("!Cannot change directory to %s\n", opt->strarg); return 1; } if(chroot(opt->strarg) != 0) { logg("!chroot to %s failed. Are you root?\n", opt->strarg); return 1; } } pt = optget(opts, "AddHeader")->strarg; if (strcasecmp(pt, "No")) { char myname[255]; if (((opt = optget(opts, "ReportHostname"))->enabled && strncpy(myname, opt->strarg, sizeof(myname))) || !gethostname(myname, sizeof(myname))) { myname[sizeof(myname)-1] = '\0'; snprintf(xvirushdr, sizeof(xvirushdr), "clamav-milter %s at %s", get_version(), myname); } else { snprintf(xvirushdr, sizeof(xvirushdr), "clamav-milter %s", get_version()); } xvirushdr[sizeof(xvirushdr)-1] = '\0'; descr.xxfi_flags |= SMFIF_ADDHDRS; if (strcasecmp(pt, "Add")) { /* Replace or Yes */ descr.xxfi_flags |= SMFIF_CHGHDRS; addxvirus = 1; } else { /* Add */ addxvirus = 2; } } if(!(my_socket = optget(opts, "MilterSocket")->strarg)) { logg("!Please configure the MilterSocket directive\n"); logg_close(); optfree(opts); return 1; } if(smfi_setconn(my_socket) == MI_FAILURE) { logg("!smfi_setconn failed\n"); logg_close(); optfree(opts); return 1; } if(smfi_register(descr) == MI_FAILURE) { logg("!smfi_register failed\n"); logg_close(); optfree(opts); return 1; } opt = optget(opts, "FixStaleSocket"); umsk = umask(0777); /* socket is created with 000 to avoid races */ if(smfi_opensocket(opt->enabled) == MI_FAILURE) { logg("!Failed to create socket %s\n", my_socket); logg_close(); optfree(opts); return 1; } umask(umsk); /* restore umask */ if(strncmp(my_socket, "inet:", 5) && strncmp(my_socket, "inet6:", 6)) { /* set group ownership and perms on the local socket */ char *sock_name = my_socket; mode_t sock_mode; if(!strncmp(my_socket, "unix:", 5)) sock_name += 5; if(!strncmp(my_socket, "local:", 6)) sock_name += 6; if(*my_socket == ':') sock_name ++; if(optget(opts, "MilterSocketGroup")->enabled) { char *gname = optget(opts, "MilterSocketGroup")->strarg, *end; gid_t sock_gid = strtol(gname, &end, 10); if(*end) { struct group *pgrp = getgrnam(gname); if(!pgrp) { logg("!Unknown group %s\n", gname); logg_close(); optfree(opts); return 1; } sock_gid = pgrp->gr_gid; } if(chown(sock_name, -1, sock_gid)) { logg("!Failed to change socket ownership to group %s\n", gname); logg_close(); optfree(opts); return 1; } } if ((opt = optget(opts, "User"))->enabled) { struct passwd *user; if ((user = getpwnam(opt->strarg)) == NULL) { logg("ERROR: Can't get information about user %s.\n", opt->strarg); logg_close(); optfree(opts); return 1; } if(chown(sock_name, user->pw_uid, -1)) { logg("!Failed to change socket ownership to user %s\n", user->pw_name); optfree(opts); logg_close(); return 1; } } if(optget(opts, "MilterSocketMode")->enabled) { char *end; sock_mode = strtol(optget(opts, "MilterSocketMode")->strarg, &end, 8); if(*end) { logg("!Invalid MilterSocketMode %s\n", optget(opts, "MilterSocketMode")->strarg); logg_close(); optfree(opts); return 1; } } else sock_mode = 0777 & ~umsk; if(chmod(sock_name, sock_mode & 0666)) { logg("!Cannot set milter socket permission to %s\n", optget(opts, "MilterSocketMode")->strarg); logg_close(); optfree(opts); return 1; } } if(geteuid() == 0 && (opt = optget(opts, "User"))->enabled) { struct passwd *user = NULL; if((user = getpwnam(opt->strarg)) == NULL) { fprintf(stderr, "ERROR: Can't get information about user %s.\n", opt->strarg); optfree(opts); return 1; } if(optget(opts, "AllowSupplementaryGroups")->enabled) { #ifdef HAVE_INITGROUPS if(initgroups(opt->strarg, user->pw_gid)) { fprintf(stderr, "ERROR: initgroups() failed.\n"); optfree(opts); return 1; } #else mprintf("!AllowSupplementaryGroups: initgroups() is not available, please disable AllowSupplementaryGroups\n"); optfree(opts); return 1; #endif } else { #ifdef HAVE_SETGROUPS if(setgroups(1, &user->pw_gid)) { fprintf(stderr, "ERROR: setgroups() failed.\n"); optfree(opts); return 1; } #endif } if(setgid(user->pw_gid)) { fprintf(stderr, "ERROR: setgid(%d) failed.\n", (int) user->pw_gid); optfree(opts); return 1; } if(setuid(user->pw_uid)) { fprintf(stderr, "ERROR: setuid(%d) failed.\n", (int) user->pw_uid); optfree(opts); return 1; } } logg_lock = !optget(opts, "LogFileUnlock")->enabled; logg_time = optget(opts, "LogTime")->enabled; logg_size = optget(opts, "LogFileMaxSize")->numarg; logg_verbose = mprintf_verbose = optget(opts, "LogVerbose")->enabled; if (logg_size) logg_rotate = optget(opts, "LogRotate")->enabled; if((opt = optget(opts, "LogFile"))->enabled) { logg_file = opt->strarg; if(!cli_is_abspath(logg_file)) { fprintf(stderr, "ERROR: LogFile requires full path.\n"); logg_close(); optfree(opts); return 1; } } else logg_file = NULL; #if defined(USE_SYSLOG) && !defined(C_AIX) if(optget(opts, "LogSyslog")->enabled) { int fac; opt = optget(opts, "LogFacility"); if((fac = logg_facility(opt->strarg)) == -1) { logg("!LogFacility: %s: No such facility.\n", opt->strarg); logg_close(); optfree(opts); return 1; } openlog("clamav-milter", LOG_PID, fac); logg_syslog = 1; } #endif time(&currtime); if(logg("#+++ Started at %s", ctime(&currtime))) { fprintf(stderr, "ERROR: Can't initialize the internal logger\n"); logg_close(); optfree(opts); return 1; } if((opt = optget(opts, "TemporaryDirectory"))->enabled) tempdir = opt->strarg; if(localnets_init(opts) || init_actions(opts)) { logg_close(); optfree(opts); return 1; } if((opt = optget(opts, "Whitelist"))->enabled && whitelist_init(opt->strarg)) { localnets_free(); logg_close(); optfree(opts); return 1; } if((opt = optget(opts, "SkipAuthenticated"))->enabled && smtpauth_init(opt->strarg)) { localnets_free(); whitelist_free(); logg_close(); optfree(opts); return 1; } multircpt = optget(opts, "SupportMultipleRecipients")->enabled; if(!optget(opts, "Foreground")->enabled) { if(daemonize() == -1) { logg("!daemonize() failed\n"); localnets_free(); whitelist_free(); cpool_free(); logg_close(); optfree(opts); return 1; } if(chdir("/") == -1) logg("^Can't change current working directory to root\n"); } maxfilesize = optget(opts, "MaxFileSize")->numarg; if(!maxfilesize) { logg("^Invalid MaxFileSize, using default (%d)\n", CLI_DEFAULT_MAXFILESIZE); maxfilesize = CLI_DEFAULT_MAXFILESIZE; } readtimeout = optget(opts, "ReadTimeout")->numarg; cpool_init(opts); if (!cp) { logg("!Failed to init the socket pool\n"); localnets_free(); whitelist_free(); logg_close(); optfree(opts); return 1; } if((opt = optget(opts, "PidFile"))->enabled) { FILE *fd; mode_t old_umask = umask(0002); if((fd = fopen(opt->strarg, "w")) == NULL) { logg("!Can't save PID in file %s\n", opt->strarg); } else { if (fprintf(fd, "%u\n", (unsigned int)getpid())<0) { logg("!Can't save PID in file %s\n", opt->strarg); } fclose(fd); } umask(old_umask); } ret = smfi_main(); optfree(opts); logg_close(); cpool_free(); localnets_free(); whitelist_free(); return ret; }
pid_t _SCDPluginExecCommand2(SCDPluginExecCallBack callout, void *context, uid_t uid, gid_t gid, const char *path, char * const argv[], SCDPluginExecSetup setup, void *setupContext ) { char buf[1024]; pid_t pid; struct passwd pwd; struct passwd *result = NULL; char *username = NULL; // grab the activeChildren mutex pthread_mutex_lock(&lock); // cache the getpwuid_r result here to avoid spinning that can happen // when calling it between fork and execv. if ((getpwuid_r(uid, &pwd, buf, sizeof(buf), &result) == 0) && (result != NULL)) { username = result->pw_name; } // if needed, initialize if (childReaped == NULL) { _SCDPluginExecInit(); } pid = fork(); switch (pid) { case -1 : { /* if error */ int status; status = errno; printf("fork() failed: %s\n", strerror(status)); errno = status; break; } case 0 : { /* if child */ gid_t egid; uid_t euid; int i; int status; if (setup != NULL) { (setup)(pid, setupContext); } else { /* close any open FDs */ for (i = getdtablesize()-1; i>=0; i--) close(i); open(_PATH_DEVNULL, O_RDWR, 0); dup(0); dup(0); } egid = getegid(); euid = geteuid(); if (egid != gid) { (void) setgid(gid); } if (((euid != uid) || (egid != gid)) && username) { initgroups(username, gid); } if (euid != uid) { (void) setuid(uid); } /* ensure that our PATH environment variable is somewhat reasonable */ if (setenv("PATH", "/bin:/sbin:/usr/bin:/usr/sbin", 0) == -1) { printf("setenv() failed: %s\n", strerror(errno)); exit(EX_OSERR); } /* execute requested command */ (void) execv(path, argv); /* if the execv failed */ status = W_EXITCODE(errno, 0); _exit (WEXITSTATUS(status)); } default : { /* if parent */ if (setup != NULL) { (setup)(pid, setupContext); } if (callout != NULL) { childInfoRef child; // create child process info child = CFAllocatorAllocate(NULL, sizeof(struct childInfo), 0); bzero(child, sizeof(struct childInfo)); child->pid = pid; child->callout = callout; child->context = context; // add the new child to the activeChildren list child->next = activeChildren; activeChildren = child; } break; } } // release the activeChildren mutex pthread_mutex_unlock(&lock); return pid; }
static void drop_privileges(struct credentials *cred) { if (cred && (initgroups(cred->pass->pw_name, cred->gid) || setgid (cred->gid) || setuid(cred->pass->pw_uid))) die("cannot drop privileges"); }
/** daemonize, drop user privileges and chroot if needed */ static void perform_setup(struct daemon* daemon, struct config_file* cfg, int debug_mode, const char** cfgfile, int need_pidfile) { #ifdef HAVE_KILL int pidinchroot; #endif #ifdef HAVE_GETPWNAM struct passwd *pwd = NULL; if(cfg->username && cfg->username[0]) { if((pwd = getpwnam(cfg->username)) == NULL) fatal_exit("user '%s' does not exist.", cfg->username); /* endpwent below, in case we need pwd for setusercontext */ } #endif #ifdef UB_ON_WINDOWS w_config_adjust_directory(cfg); #endif /* read ssl keys while superuser and outside chroot */ #ifdef HAVE_SSL if(!(daemon->rc = daemon_remote_create(cfg))) fatal_exit("could not set up remote-control"); if(cfg->ssl_service_key && cfg->ssl_service_key[0]) { if(!(daemon->listen_sslctx = listen_sslctx_create( cfg->ssl_service_key, cfg->ssl_service_pem, NULL))) fatal_exit("could not set up listen SSL_CTX"); if(cfg->tls_ciphers && cfg->tls_ciphers[0]) { if (!SSL_CTX_set_cipher_list(daemon->listen_sslctx, cfg->tls_ciphers)) { fatal_exit("failed to set tls-cipher %s", cfg->tls_ciphers); } } #ifdef HAVE_SSL_CTX_SET_CIPHERSUITES if(cfg->tls_ciphersuites && cfg->tls_ciphersuites[0]) { if (!SSL_CTX_set_ciphersuites(daemon->listen_sslctx, cfg->tls_ciphersuites)) { fatal_exit("failed to set tls-ciphersuites %s", cfg->tls_ciphersuites); } } #endif if(cfg->tls_session_ticket_keys.first && cfg->tls_session_ticket_keys.first->str[0] != 0) { if(!listen_sslctx_setup_ticket_keys(daemon->listen_sslctx, cfg->tls_session_ticket_keys.first)) { fatal_exit("could not set session ticket SSL_CTX"); } } } if(!(daemon->connect_sslctx = connect_sslctx_create(NULL, NULL, cfg->tls_cert_bundle, cfg->tls_win_cert))) fatal_exit("could not set up connect SSL_CTX"); #endif /* init syslog (as root) if needed, before daemonize, otherwise * a fork error could not be printed since daemonize closed stderr.*/ if(cfg->use_syslog) { log_init(cfg->logfile, cfg->use_syslog, cfg->chrootdir); } /* if using a logfile, we cannot open it because the logfile would * be created with the wrong permissions, we cannot chown it because * we cannot chown system logfiles, so we do not open at all. * So, using a logfile, the user does not see errors unless -d is * given to unbound on the commandline. */ #ifdef HAVE_KILL /* true if pidfile is inside chrootdir, or nochroot */ pidinchroot = need_pidfile && (!(cfg->chrootdir && cfg->chrootdir[0]) || (cfg->chrootdir && cfg->chrootdir[0] && strncmp(cfg->pidfile, cfg->chrootdir, strlen(cfg->chrootdir))==0)); /* check old pid file before forking */ if(cfg->pidfile && cfg->pidfile[0] && need_pidfile) { /* calculate position of pidfile */ if(cfg->pidfile[0] == '/') daemon->pidfile = strdup(cfg->pidfile); else daemon->pidfile = fname_after_chroot(cfg->pidfile, cfg, 1); if(!daemon->pidfile) fatal_exit("pidfile alloc: out of memory"); checkoldpid(daemon->pidfile, pidinchroot); } #endif /* daemonize because pid is needed by the writepid func */ if(!debug_mode && cfg->do_daemonize) { detach(); } /* write new pidfile (while still root, so can be outside chroot) */ #ifdef HAVE_KILL if(cfg->pidfile && cfg->pidfile[0] && need_pidfile) { writepid(daemon->pidfile, getpid()); if(cfg->username && cfg->username[0] && cfg_uid != (uid_t)-1 && pidinchroot) { # ifdef HAVE_CHOWN if(chown(daemon->pidfile, cfg_uid, cfg_gid) == -1) { verbose(VERB_QUERY, "cannot chown %u.%u %s: %s", (unsigned)cfg_uid, (unsigned)cfg_gid, daemon->pidfile, strerror(errno)); } # endif /* HAVE_CHOWN */ } } #else (void)daemon; (void)need_pidfile; #endif /* HAVE_KILL */ /* Set user context */ #ifdef HAVE_GETPWNAM if(cfg->username && cfg->username[0] && cfg_uid != (uid_t)-1) { #ifdef HAVE_SETUSERCONTEXT /* setusercontext does initgroups, setuid, setgid, and * also resource limits from login config, but we * still call setresuid, setresgid to be sure to set all uid*/ if(setusercontext(NULL, pwd, cfg_uid, (unsigned) LOGIN_SETALL & ~LOGIN_SETUSER & ~LOGIN_SETGROUP) != 0) log_warn("unable to setusercontext %s: %s", cfg->username, strerror(errno)); #endif /* HAVE_SETUSERCONTEXT */ } #endif /* HAVE_GETPWNAM */ /* box into the chroot */ #ifdef HAVE_CHROOT if(cfg->chrootdir && cfg->chrootdir[0]) { if(chdir(cfg->chrootdir)) { fatal_exit("unable to chdir to chroot %s: %s", cfg->chrootdir, strerror(errno)); } verbose(VERB_QUERY, "chdir to %s", cfg->chrootdir); if(chroot(cfg->chrootdir)) fatal_exit("unable to chroot to %s: %s", cfg->chrootdir, strerror(errno)); if(chdir("/")) fatal_exit("unable to chdir to / in chroot %s: %s", cfg->chrootdir, strerror(errno)); verbose(VERB_QUERY, "chroot to %s", cfg->chrootdir); if(strncmp(*cfgfile, cfg->chrootdir, strlen(cfg->chrootdir)) == 0) (*cfgfile) += strlen(cfg->chrootdir); /* adjust stored pidfile for chroot */ if(daemon->pidfile && daemon->pidfile[0] && strncmp(daemon->pidfile, cfg->chrootdir, strlen(cfg->chrootdir))==0) { char* old = daemon->pidfile; daemon->pidfile = strdup(old+strlen(cfg->chrootdir)); free(old); if(!daemon->pidfile) log_err("out of memory in pidfile adjust"); } daemon->chroot = strdup(cfg->chrootdir); if(!daemon->chroot) log_err("out of memory in daemon chroot dir storage"); } #else (void)cfgfile; #endif /* change to working directory inside chroot */ if(cfg->directory && cfg->directory[0]) { char* dir = cfg->directory; if(cfg->chrootdir && cfg->chrootdir[0] && strncmp(dir, cfg->chrootdir, strlen(cfg->chrootdir)) == 0) dir += strlen(cfg->chrootdir); if(dir[0]) { if(chdir(dir)) { fatal_exit("Could not chdir to %s: %s", dir, strerror(errno)); } verbose(VERB_QUERY, "chdir to %s", dir); } } /* drop permissions after chroot, getpwnam, pidfile, syslog done*/ #ifdef HAVE_GETPWNAM if(cfg->username && cfg->username[0] && cfg_uid != (uid_t)-1) { # ifdef HAVE_INITGROUPS if(initgroups(cfg->username, cfg_gid) != 0) log_warn("unable to initgroups %s: %s", cfg->username, strerror(errno)); # endif /* HAVE_INITGROUPS */ # ifdef HAVE_ENDPWENT endpwent(); # endif #ifdef HAVE_SETRESGID if(setresgid(cfg_gid,cfg_gid,cfg_gid) != 0) #elif defined(HAVE_SETREGID) && !defined(DARWIN_BROKEN_SETREUID) if(setregid(cfg_gid,cfg_gid) != 0) #else /* use setgid */ if(setgid(cfg_gid) != 0) #endif /* HAVE_SETRESGID */ fatal_exit("unable to set group id of %s: %s", cfg->username, strerror(errno)); #ifdef HAVE_SETRESUID if(setresuid(cfg_uid,cfg_uid,cfg_uid) != 0) #elif defined(HAVE_SETREUID) && !defined(DARWIN_BROKEN_SETREUID) if(setreuid(cfg_uid,cfg_uid) != 0) #else /* use setuid */ if(setuid(cfg_uid) != 0) #endif /* HAVE_SETRESUID */ fatal_exit("unable to set user id of %s: %s", cfg->username, strerror(errno)); verbose(VERB_QUERY, "drop user privileges, run as %s", cfg->username); } #endif /* HAVE_GETPWNAM */ /* file logging inited after chroot,chdir,setuid is done so that * it would succeed on SIGHUP as well */ if(!cfg->use_syslog) log_init(cfg->logfile, cfg->use_syslog, cfg->chrootdir); }
void slap_init_user( char *user, char *group ) { uid_t uid = 0; gid_t gid = 0; int got_uid = 0, got_gid = 0; if ( user ) { struct passwd *pwd; if ( isdigit( (unsigned char) *user ) ) { unsigned u; got_uid = 1; if ( lutil_atou( &u, user ) != 0 ) { Debug( LDAP_DEBUG_ANY, "Unble to parse user %s\n", user ); exit( EXIT_FAILURE ); } uid = (uid_t)u; #ifdef HAVE_GETPWUID pwd = getpwuid( uid ); goto did_getpw; #else free( user ); user = NULL; #endif } else { pwd = getpwnam( user ); did_getpw: if ( pwd == NULL ) { Debug( LDAP_DEBUG_ANY, "No passwd entry for user %s\n", user ); exit( EXIT_FAILURE ); } if ( got_uid ) { free( user ); user = (pwd != NULL ? ch_strdup( pwd->pw_name ) : NULL); } else { got_uid = 1; uid = pwd->pw_uid; } got_gid = 1; gid = pwd->pw_gid; #ifdef HAVE_ENDPWENT endpwent(); #endif } } if ( group ) { struct group *grp; if ( isdigit( (unsigned char) *group )) { unsigned g; if ( lutil_atou( &g, group ) != 0 ) { Debug( LDAP_DEBUG_ANY, "Unble to parse group %s\n", group ); exit( EXIT_FAILURE ); } gid = (uid_t)g; #ifdef HAVE_GETGRGID grp = getgrgid( gid ); goto did_group; #endif } else { grp = getgrnam( group ); if ( grp != NULL ) gid = grp->gr_gid; did_group: if ( grp == NULL ) { Debug( LDAP_DEBUG_ANY, "No group entry for group %s\n", group ); exit( EXIT_FAILURE ); } } free( group ); got_gid = 1; } if ( user ) { if ( getuid() == 0 && initgroups( user, gid ) != 0 ) { Debug( LDAP_DEBUG_ANY, "Could not set the group access (gid) list\n" ); exit( EXIT_FAILURE ); } free( user ); } #ifdef HAVE_ENDGRENT endgrent(); #endif if ( got_gid ) { if ( setgid( gid ) != 0 ) { Debug( LDAP_DEBUG_ANY, "Could not set real group id to %d\n", (int) gid ); exit( EXIT_FAILURE ); } #ifdef HAVE_SETEGID if ( setegid( gid ) != 0 ) { Debug( LDAP_DEBUG_ANY, "Could not set effective group id to %d\n", (int) gid ); exit( EXIT_FAILURE ); } #endif } if ( got_uid ) { if ( setuid( uid ) != 0 ) { Debug( LDAP_DEBUG_ANY, "Could not set real user id to %d\n", (int) uid ); exit( EXIT_FAILURE ); } #ifdef HAVE_SETEUID if ( seteuid( uid ) != 0 ) { Debug( LDAP_DEBUG_ANY, "Could not set effective user id to %d\n", (int) uid ); exit( EXIT_FAILURE ); } #endif } }
int main(int argc, char *argv[]) { GMainLoop *main_loop = NULL; GObject *scgi_server = NULL; static gchar *conf_dir = NULL; static gboolean debug = FALSE; #ifdef G_OS_UNIX static gchar *user = NULL; static gchar *group = NULL; #endif /* G_OS_UNIX */ static GOptionEntry option_entries[] = { { "conf", 'c', 0, G_OPTION_ARG_STRING, &conf_dir, "Config directory", NULL}, #ifdef G_OS_UNIX { "user", 'u', 0, G_OPTION_ARG_STRING, &user, "User name", NULL}, { "group", 'g', 0, G_OPTION_ARG_STRING, &group, "Group name", NULL}, #endif /* G_OS_UNIX */ { "debug", 'd', 0, G_OPTION_ARG_NONE, &debug, "Debug mode", NULL}, { NULL } }; GOptionContext *option_context = NULL; GError *error = NULL; #ifndef GLIB_VERSION_2_36 g_type_init(); #endif if(!g_module_supported()) g_error("%s:%d[%s]", __FILE__, __LINE__, __FUNCTION__); option_context = g_option_context_new(""); g_option_context_add_main_entries(option_context, option_entries, NULL); if(!g_option_context_parse(option_context, &argc, &argv, &error)) { g_error("%s:%d[%s]=>(%s)", __FILE__, __LINE__, __FUNCTION__, error->message); g_error_free(error); } g_option_context_free(option_context); #ifdef G_OS_UNIX if(group) { struct group *grp = NULL; if(NULL == (grp = getgrnam(group))) g_error("%s:%d[%s]=>(%s)", __FILE__, __LINE__, __FUNCTION__, "Get group failed!"); if(-1 == setgid(grp->gr_gid)) g_error("%s:%d[%s]=>(%s)", __FILE__, __LINE__, __FUNCTION__, "Set gid failed!"); if(-1 == setgroups(0, NULL)) g_error("%s:%d[%s]=>(%s)", __FILE__, __LINE__, __FUNCTION__, "Set groups failed!"); if(user) { if(-1 == initgroups(user, grp->gr_gid)) g_error("%s:%d[%s]=>(%s)", __FILE__, __LINE__, __FUNCTION__, "Init groups failed!"); } } if(user) { struct passwd *pwd = NULL; if(NULL == (pwd = getpwnam(user))) g_error("%s:%d[%s]=>(%s)", __FILE__, __LINE__, __FUNCTION__, "Get user failed!"); if(-1 == setuid(pwd->pw_uid)) g_error("%s:%d[%s]=>(%s)", __FILE__, __LINE__, __FUNCTION__, "Set uid failed!"); } #endif /* G_OS_UNIX */ if(!debug) { g_log_set_handler(NULL, G_LOG_LEVEL_DEBUG, debug_log_handler, NULL); #ifdef G_OS_UNIX daemon(1, 1); #endif /* G_OS_UNIX */ } main_loop = g_main_loop_new(NULL, FALSE); if(!main_loop) g_error("%s:%d[%s]", __FILE__, __LINE__, __FUNCTION__); scgi_server = hev_scgi_server_new(conf_dir); if(!scgi_server) g_error("%s:%d[%s]", __FILE__, __LINE__, __FUNCTION__); hev_scgi_server_start(HEV_SCGI_SERVER(scgi_server)); #ifdef G_OS_UNIX g_unix_signal_add(SIGINT, unix_signal_handler, main_loop); g_unix_signal_add(SIGTERM, unix_signal_handler, main_loop); #endif /* G_OS_UNIX */ g_main_loop_run(main_loop); hev_scgi_server_stop(HEV_SCGI_SERVER(scgi_server)); g_object_unref(G_OBJECT(scgi_server)); g_main_loop_unref(main_loop); return 0; }
int main(int argc, char *argv[]) { int f_dayAfter = 0; /* days after current date */ int f_dayBefore = 0; /* days before current date */ int Friday = 5; /* day before weekend */ int ch; struct tm tp1, tp2; (void)setlocale(LC_ALL, ""); while ((ch = getopt(argc, argv, "-A:aB:D:dF:f:l:t:U:W:?")) != -1) switch (ch) { case '-': /* backward contemptible */ case 'a': if (getuid()) { errno = EPERM; err(1, NULL); } doall = 1; break; case 'W': /* we don't need no steenking Fridays */ Friday = -1; /* FALLTHROUGH */ case 'A': /* days after current date */ f_dayAfter = atoi(optarg); break; case 'B': /* days before current date */ f_dayBefore = atoi(optarg); break; case 'D': /* debug output of sun and moon info */ DEBUG = optarg; break; case 'd': /* debug output of current date */ debug = 1; break; case 'F': /* Change the time: When does weekend start? */ Friday = atoi(optarg); break; case 'f': /* other calendar file */ calendarFile = optarg; break; case 'l': /* Change longitudal position */ EastLongitude = strtol(optarg, NULL, 10); break; case 't': /* other date, for tests */ f_time = Mktime(optarg); break; case 'U': /* Change UTC offset */ UTCOffset = strtod(optarg, NULL); break; case '?': default: usage(); } argc -= optind; argv += optind; if (argc) usage(); /* use current time */ if (f_time <= 0) (void)time(&f_time); /* if not set, determine where I could be */ { if (UTCOffset == UTCOFFSET_NOTSET && EastLongitude == LONGITUDE_NOTSET) { /* Calculate on difference between here and UTC */ time_t t; struct tm tm; long utcoffset, hh, mm, ss; double uo; time(&t); localtime_r(&t, &tm); utcoffset = tm.tm_gmtoff; /* seconds -> hh:mm:ss */ hh = utcoffset / SECSPERHOUR; utcoffset %= SECSPERHOUR; mm = utcoffset / SECSPERMINUTE; utcoffset %= SECSPERMINUTE; ss = utcoffset; /* hh:mm:ss -> hh.mmss */ uo = mm + (100.0 * (ss / 60.0)); uo /= 60.0 / 100.0; uo = hh + uo / 100; UTCOffset = uo; EastLongitude = UTCOffset * 15; } else if (UTCOffset == UTCOFFSET_NOTSET) { /* Base on information given */ UTCOffset = EastLongitude / 15; } else if (EastLongitude == LONGITUDE_NOTSET) { /* Base on information given */ EastLongitude = UTCOffset * 15; } } settimes(f_time, f_dayBefore, f_dayAfter, Friday, &tp1, &tp2); generatedates(&tp1, &tp2); /* * FROM now on, we are working in UTC. * This will only affect moon and sun related events anyway. */ if (setenv("TZ", "UTC", 1) != 0) errx(1, "setenv: %s", strerror(errno)); tzset(); if (debug) dumpdates(); if (DEBUG != NULL) { dodebug(DEBUG); exit(0); } if (doall) while ((pw = getpwent()) != NULL) { (void)setegid(pw->pw_gid); (void)initgroups(pw->pw_name, pw->pw_gid); (void)seteuid(pw->pw_uid); if (!chdir(pw->pw_dir)) cal(); (void)seteuid(0); } else cal(); exit(0); }
int main (int argc, char **argv) { pam_handle_t *pamh = NULL; struct passwd *pw; const char *auth; int status; int flags; int res; if (isatty (0)) errx (2, "this command is not meant to be run from the console"); if (argc != 3) errx (2, "invalid arguments to cockpit-session"); /* When setuid root, make sure our group is also root */ if (geteuid () == 0) { /* Never trust the environment when running setuid() */ if (getuid() != 0) { if (clearenv () != 0) err (1, "couldn't clear environment"); /* set a minimal environment */ setenv ("PATH", "/usr/sbin:/usr/bin:/sbin:/bin", 1); } if (setgid (0) != 0 || setuid (0) != 0) err (1, "couldn't switch permissions correctly"); } /* We should never leak our auth fd to other processes */ flags = fcntl (AUTH_FD, F_GETFD); if (flags < 0 || fcntl (AUTH_FD, F_SETFD, flags | FD_CLOEXEC)) err (1, "couldn't set auth fd flags"); auth = argv[1]; rhost = argv[2]; signal (SIGALRM, SIG_DFL); signal (SIGQUIT, SIG_DFL); signal (SIGTSTP, SIG_IGN); signal (SIGHUP, SIG_IGN); signal (SIGPIPE, SIG_IGN); snprintf (line, UT_LINESIZE, "cockpit-%d", getpid ()); line[UT_LINESIZE] = '\0'; if (strcmp (auth, "basic") == 0) pamh = perform_basic (); else if (strcmp (auth, "negotiate") == 0) pamh = perform_gssapi (); else errx (2, "unrecognized authentication method: %s", auth); if (want_session) { /* Let the G_MESSAGES_DEBUG leak through from parent as a default */ transfer_pam_env (pamh, "G_DEBUG", "G_MESSAGES_DEBUG", NULL); env = pam_getenvlist (pamh); if (env == NULL) errx (EX, "get pam environment failed"); pw = getpwnam (user); if (pw == NULL) errx (EX, "%s: invalid user", user); if (initgroups (user, pw->pw_gid) < 0) err (EX, "%s: can't init groups", user); signal (SIGTERM, pass_to_child); signal (SIGINT, pass_to_child); signal (SIGQUIT, pass_to_child); utmp_log (1); status = fork_session (pw, session); utmp_log (0); signal (SIGTERM, SIG_DFL); signal (SIGINT, SIG_DFL); signal (SIGQUIT, SIG_DFL); res = pam_setcred (pamh, PAM_DELETE_CRED); if (res != PAM_SUCCESS) err (EX, "%s: couldn't delete creds: %s", user, pam_strerror (pamh, res)); res = pam_close_session (pamh, 0); if (res != PAM_SUCCESS) err (EX, "%s: couldn't close session: %s", user, pam_strerror (pamh, res)); } else { status = session (); } pam_end (pamh, PAM_SUCCESS); if (WIFEXITED(status)) exit (WEXITSTATUS(status)); else if (WIFSIGNALED(status)) raise (WTERMSIG(status)); else exit (127); }
RTR3DECL(int) RTProcCreateEx(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags, PCRTHANDLE phStdIn, PCRTHANDLE phStdOut, PCRTHANDLE phStdErr, const char *pszAsUser, const char *pszPassword, PRTPROCESS phProcess) { int rc; /* * Input validation */ AssertPtrReturn(pszExec, VERR_INVALID_POINTER); AssertReturn(*pszExec, VERR_INVALID_PARAMETER); AssertReturn(!(fFlags & ~RTPROC_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER); AssertReturn(!(fFlags & RTPROC_FLAGS_DETACHED) || !phProcess, VERR_INVALID_PARAMETER); AssertReturn(hEnv != NIL_RTENV, VERR_INVALID_PARAMETER); AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER); AssertPtrNullReturn(pszAsUser, VERR_INVALID_POINTER); AssertReturn(!pszAsUser || *pszAsUser, VERR_INVALID_PARAMETER); AssertReturn(!pszPassword || pszAsUser, VERR_INVALID_PARAMETER); AssertPtrNullReturn(pszPassword, VERR_INVALID_POINTER); #if defined(RT_OS_OS2) if (fFlags & RTPROC_FLAGS_DETACHED) return VERR_PROC_DETACH_NOT_SUPPORTED; #endif /* * Get the file descriptors for the handles we've been passed. */ PCRTHANDLE paHandles[3] = { phStdIn, phStdOut, phStdErr }; int aStdFds[3] = { -1, -1, -1 }; for (int i = 0; i < 3; i++) { if (paHandles[i]) { AssertPtrReturn(paHandles[i], VERR_INVALID_POINTER); switch (paHandles[i]->enmType) { case RTHANDLETYPE_FILE: aStdFds[i] = paHandles[i]->u.hFile != NIL_RTFILE ? (int)RTFileToNative(paHandles[i]->u.hFile) : -2 /* close it */; break; case RTHANDLETYPE_PIPE: aStdFds[i] = paHandles[i]->u.hPipe != NIL_RTPIPE ? (int)RTPipeToNative(paHandles[i]->u.hPipe) : -2 /* close it */; break; case RTHANDLETYPE_SOCKET: aStdFds[i] = paHandles[i]->u.hSocket != NIL_RTSOCKET ? (int)RTSocketToNative(paHandles[i]->u.hSocket) : -2 /* close it */; break; default: AssertMsgFailedReturn(("%d: %d\n", i, paHandles[i]->enmType), VERR_INVALID_PARAMETER); } /** @todo check the close-on-execness of these handles? */ } } for (int i = 0; i < 3; i++) if (aStdFds[i] == i) aStdFds[i] = -1; for (int i = 0; i < 3; i++) AssertMsgReturn(aStdFds[i] < 0 || aStdFds[i] > i, ("%i := %i not possible because we're lazy\n", i, aStdFds[i]), VERR_NOT_SUPPORTED); /* * Resolve the user id if specified. */ uid_t uid = ~(uid_t)0; gid_t gid = ~(gid_t)0; if (pszAsUser) { rc = rtCheckCredentials(pszAsUser, pszPassword, &gid, &uid); if (RT_FAILURE(rc)) return rc; } /* * Create the child environment if either RTPROC_FLAGS_PROFILE or * RTPROC_FLAGS_ENV_CHANGE_RECORD are in effect. */ RTENV hEnvToUse = hEnv; if ( (fFlags & (RTPROC_FLAGS_ENV_CHANGE_RECORD | RTPROC_FLAGS_PROFILE)) && ( (fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD) || hEnv == RTENV_DEFAULT) ) { if (fFlags & RTPROC_FLAGS_PROFILE) rc = rtProcPosixCreateProfileEnv(&hEnvToUse, pszAsUser); else rc = RTEnvClone(&hEnvToUse, RTENV_DEFAULT); if (RT_SUCCESS(rc)) { if ((fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD) && hEnv != RTENV_DEFAULT) rc = RTEnvApplyChanges(hEnvToUse, hEnv); if (RT_FAILURE(rc)) RTEnvDestroy(hEnvToUse); } if (RT_FAILURE(rc)) return rc; } /* * Check for execute access to the file. */ char szRealExec[RTPATH_MAX]; if (access(pszExec, X_OK)) { rc = errno; if ( !(fFlags & RTPROC_FLAGS_SEARCH_PATH) || rc != ENOENT || RTPathHavePath(pszExec) ) rc = RTErrConvertFromErrno(rc); else { /* search */ char *pszPath = RTEnvDupEx(hEnvToUse, "PATH"); rc = RTPathTraverseList(pszPath, ':', rtPathFindExec, (void *)pszExec, &szRealExec[0]); RTStrFree(pszPath); if (RT_SUCCESS(rc)) pszExec = szRealExec; else rc = rc == VERR_END_OF_STRING ? VERR_FILE_NOT_FOUND : rc; } if (RT_FAILURE(rc)) return rtProcPosixCreateReturn(rc, hEnvToUse, hEnv); } pid_t pid = -1; const char * const *papszEnv = RTEnvGetExecEnvP(hEnvToUse); AssertPtrReturn(papszEnv, rtProcPosixCreateReturn(VERR_INVALID_HANDLE, hEnvToUse, hEnv)); /* * Take care of detaching the process. * * HACK ALERT! Put the process into a new process group with pgid = pid * to make sure it differs from that of the parent process to ensure that * the IPRT waitpid call doesn't race anyone (read XPCOM) doing group wide * waits. setsid() includes the setpgid() functionality. * 2010-10-11 XPCOM no longer waits for anything, but it cannot hurt. */ #ifndef RT_OS_OS2 if (fFlags & RTPROC_FLAGS_DETACHED) { # ifdef RT_OS_SOLARIS int templateFd = -1; if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT)) { templateFd = rtSolarisContractPreFork(); if (templateFd == -1) return rtProcPosixCreateReturn(VERR_OPEN_FAILED, hEnvToUse, hEnv); } # endif /* RT_OS_SOLARIS */ pid = fork(); if (!pid) { # ifdef RT_OS_SOLARIS if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT)) rtSolarisContractPostForkChild(templateFd); # endif setsid(); /* see comment above */ pid = -1; /* Child falls through to the actual spawn code below. */ } else { # ifdef RT_OS_SOLARIS if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT)) rtSolarisContractPostForkParent(templateFd, pid); # endif if (pid > 0) { /* Must wait for the temporary process to avoid a zombie. */ int status = 0; pid_t pidChild = 0; /* Restart if we get interrupted. */ do { pidChild = waitpid(pid, &status, 0); } while ( pidChild == -1 && errno == EINTR); /* Assume that something wasn't found. No detailed info. */ if (status) return rtProcPosixCreateReturn(VERR_PROCESS_NOT_FOUND, hEnvToUse, hEnv); if (phProcess) *phProcess = 0; return rtProcPosixCreateReturn(VINF_SUCCESS, hEnvToUse, hEnv); } return rtProcPosixCreateReturn(RTErrConvertFromErrno(errno), hEnvToUse, hEnv); } } #endif /* * Spawn the child. * * Any spawn code MUST not execute any atexit functions if it is for a * detached process. It would lead to running the atexit functions which * make only sense for the parent. libORBit e.g. gets confused by multiple * execution. Remember, there was only a fork() so far, and until exec() * is successfully run there is nothing which would prevent doing anything * silly with the (duplicated) file descriptors. */ #ifdef HAVE_POSIX_SPAWN /** @todo OS/2: implement DETACHED (BACKGROUND stuff), see VbglR3Daemonize. */ if ( uid == ~(uid_t)0 && gid == ~(gid_t)0) { /* Spawn attributes. */ posix_spawnattr_t Attr; rc = posix_spawnattr_init(&Attr); if (!rc) { /* Indicate that process group and signal mask are to be changed, and that the child should use default signal actions. */ rc = posix_spawnattr_setflags(&Attr, POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF); Assert(rc == 0); /* The child starts in its own process group. */ if (!rc) { rc = posix_spawnattr_setpgroup(&Attr, 0 /* pg == child pid */); Assert(rc == 0); } /* Unmask all signals. */ if (!rc) { sigset_t SigMask; sigemptyset(&SigMask); rc = posix_spawnattr_setsigmask(&Attr, &SigMask); Assert(rc == 0); } /* File changes. */ posix_spawn_file_actions_t FileActions; posix_spawn_file_actions_t *pFileActions = NULL; if ((aStdFds[0] != -1 || aStdFds[1] != -1 || aStdFds[2] != -1) && !rc) { rc = posix_spawn_file_actions_init(&FileActions); if (!rc) { pFileActions = &FileActions; for (int i = 0; i < 3; i++) { int fd = aStdFds[i]; if (fd == -2) rc = posix_spawn_file_actions_addclose(&FileActions, i); else if (fd >= 0 && fd != i) { rc = posix_spawn_file_actions_adddup2(&FileActions, fd, i); if (!rc) { for (int j = i + 1; j < 3; j++) if (aStdFds[j] == fd) { fd = -1; break; } if (fd >= 0) rc = posix_spawn_file_actions_addclose(&FileActions, fd); } } if (rc) break; } } } if (!rc) rc = posix_spawn(&pid, pszExec, pFileActions, &Attr, (char * const *)papszArgs, (char * const *)papszEnv); /* cleanup */ int rc2 = posix_spawnattr_destroy(&Attr); Assert(rc2 == 0); NOREF(rc2); if (pFileActions) { rc2 = posix_spawn_file_actions_destroy(pFileActions); Assert(rc2 == 0); } /* return on success.*/ if (!rc) { /* For a detached process this happens in the temp process, so * it's not worth doing anything as this process must exit. */ if (fFlags & RTPROC_FLAGS_DETACHED) _Exit(0); if (phProcess) *phProcess = pid; return rtProcPosixCreateReturn(VINF_SUCCESS, hEnvToUse, hEnv); } } /* For a detached process this happens in the temp process, so * it's not worth doing anything as this process must exit. */ if (fFlags & RTPROC_FLAGS_DETACHED) _Exit(124); } else #endif { #ifdef RT_OS_SOLARIS int templateFd = -1; if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT)) { templateFd = rtSolarisContractPreFork(); if (templateFd == -1) return rtProcPosixCreateReturn(VERR_OPEN_FAILED, hEnvToUse, hEnv); } #endif /* RT_OS_SOLARIS */ pid = fork(); if (!pid) { #ifdef RT_OS_SOLARIS if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT)) rtSolarisContractPostForkChild(templateFd); #endif /* RT_OS_SOLARIS */ if (!(fFlags & RTPROC_FLAGS_DETACHED)) setpgid(0, 0); /* see comment above */ /* * Change group and user if requested. */ #if 1 /** @todo This needs more work, see suplib/hardening. */ if (pszAsUser) { int ret = initgroups(pszAsUser, gid); if (ret) { if (fFlags & RTPROC_FLAGS_DETACHED) _Exit(126); else exit(126); } } if (gid != ~(gid_t)0) { if (setgid(gid)) { if (fFlags & RTPROC_FLAGS_DETACHED) _Exit(126); else exit(126); } } if (uid != ~(uid_t)0) { if (setuid(uid)) { if (fFlags & RTPROC_FLAGS_DETACHED) _Exit(126); else exit(126); } } #endif /* * Some final profile environment tweaks, if running as user. */ if ( (fFlags & RTPROC_FLAGS_PROFILE) && pszAsUser && ( (fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD) || hEnv == RTENV_DEFAULT) ) { rc = rtProcPosixAdjustProfileEnvFromChild(hEnvToUse, fFlags, hEnv); papszEnv = RTEnvGetExecEnvP(hEnvToUse); if (RT_FAILURE(rc) || !papszEnv) { if (fFlags & RTPROC_FLAGS_DETACHED) _Exit(126); else exit(126); } } /* * Unset the signal mask. */ sigset_t SigMask; sigemptyset(&SigMask); rc = sigprocmask(SIG_SETMASK, &SigMask, NULL); Assert(rc == 0); /* * Apply changes to the standard file descriptor and stuff. */ for (int i = 0; i < 3; i++) { int fd = aStdFds[i]; if (fd == -2) close(i); else if (fd >= 0) { int rc2 = dup2(fd, i); if (rc2 != i) { if (fFlags & RTPROC_FLAGS_DETACHED) _Exit(125); else exit(125); } for (int j = i + 1; j < 3; j++) if (aStdFds[j] == fd) { fd = -1; break; } if (fd >= 0) close(fd); } } /* * Finally, execute the requested program. */ rc = execve(pszExec, (char * const *)papszArgs, (char * const *)papszEnv); if (errno == ENOEXEC) { /* This can happen when trying to start a shell script without the magic #!/bin/sh */ RTAssertMsg2Weak("Cannot execute this binary format!\n"); } else RTAssertMsg2Weak("execve returns %d errno=%d\n", rc, errno); RTAssertReleasePanic(); if (fFlags & RTPROC_FLAGS_DETACHED) _Exit(127); else exit(127); } #ifdef RT_OS_SOLARIS if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT)) rtSolarisContractPostForkParent(templateFd, pid); #endif /* RT_OS_SOLARIS */ if (pid > 0) { /* For a detached process this happens in the temp process, so * it's not worth doing anything as this process must exit. */ if (fFlags & RTPROC_FLAGS_DETACHED) _Exit(0); if (phProcess) *phProcess = pid; return rtProcPosixCreateReturn(VINF_SUCCESS, hEnvToUse, hEnv); } /* For a detached process this happens in the temp process, so * it's not worth doing anything as this process must exit. */ if (fFlags & RTPROC_FLAGS_DETACHED) _Exit(124); return rtProcPosixCreateReturn(RTErrConvertFromErrno(errno), hEnvToUse, hEnv); } return rtProcPosixCreateReturn(VERR_NOT_IMPLEMENTED, hEnvToUse, hEnv); }
/* * Do chroot, if requested. * * Switch UID and GID to what is specified in the config file */ static int switch_users(CONF_SECTION *cs) { #ifdef HAVE_SYS_RESOURCE_H /* * Get the current maximum for core files. Do this * before anything else so as to ensure it's properly * initialized. */ if (getrlimit(RLIMIT_CORE, &core_limits) < 0) { radlog(L_ERR, "Failed to get current core limit: %s", strerror(errno)); return 0; } #endif /* * Don't do chroot/setuid/setgid if we're in debugging * as non-root. */ if (debug_flag && (getuid() != 0)) return 1; if (cf_section_parse(cs, NULL, bootstrap_config) < 0) { fprintf(stderr, "radiusd: Error: Failed to parse user/group information.\n"); return 0; } #ifdef HAVE_GRP_H /* Set GID. */ if (gid_name) { struct group *gr; gr = getgrnam(gid_name); if (gr == NULL) { fprintf(stderr, "%s: Cannot get ID for group %s: %s\n", progname, gid_name, strerror(errno)); return 0; } server_gid = gr->gr_gid; } else { server_gid = getgid(); } #endif #ifdef HAVE_PWD_H /* Set UID. */ if (uid_name) { struct passwd *pw; pw = getpwnam(uid_name); if (pw == NULL) { fprintf(stderr, "%s: Cannot get passwd entry for user %s: %s\n", progname, uid_name, strerror(errno)); return 0; } if (getuid() == pw->pw_uid) { uid_name = NULL; } else { server_uid = pw->pw_uid; #ifdef HAVE_INITGROUPS if (initgroups(uid_name, server_gid) < 0) { fprintf(stderr, "%s: Cannot initialize supplementary group list for user %s: %s\n", progname, uid_name, strerror(errno)); return 0; } #endif } } else { server_uid = getuid(); } #endif if (chroot_dir) { if (chroot(chroot_dir) < 0) { fprintf(stderr, "%s: Failed to perform chroot %s: %s", progname, chroot_dir, strerror(errno)); return 0; } /* * Note that we leave chdir alone. It may be * OUTSIDE of the root. This allows us to read * the configuration from "-d ./etc/raddb", with * the chroot as "./chroot/" for example. After * the server has been loaded, it does a "cd * ${logdir}" below, so that core files (if any) * go to a logging directory. * * This also allows the configuration of the * server to be outside of the chroot. If the * server is statically linked, then the only * things needed inside of the chroot are the * logging directories. */ } #ifdef HAVE_GRP_H /* Set GID. */ if (gid_name && (setgid(server_gid) < 0)) { fprintf(stderr, "%s: Failed setting group to %s: %s", progname, gid_name, strerror(errno)); return 0; } #endif #ifdef HAVE_SETUID /* * Just before losing root permissions, ensure that the * log files have the correct owner && group. * * We have to do this because the log file MAY have been * specified on the command-line. */ if (uid_name || gid_name) { if ((mainconfig.radlog_dest == RADLOG_FILES) && (mainconfig.radlog_fd < 0)) { mainconfig.radlog_fd = open(mainconfig.log_file, O_WRONLY | O_APPEND | O_CREAT, 0640); if (mainconfig.radlog_fd < 0) { fprintf(stderr, "radiusd: Failed to open log file %s: %s\n", mainconfig.log_file, strerror(errno)); return 0; } if (chown(mainconfig.log_file, server_uid, server_gid) < 0) { fprintf(stderr, "%s: Cannot change ownership of log file %s: %s\n", progname, mainconfig.log_file, strerror(errno)); return 0; } } } if (uid_name) { doing_setuid = TRUE; fr_suid_down(); } #endif /* * This also clears the dumpable flag if core dumps * aren't allowed. */ fr_set_dumpable(); if (allow_core_dumps) { radlog(L_INFO, "Core dumps are enabled."); } return 1; }
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *init_str, void *reserved) { struct passwd *pwd = NULL; opts_t opts; if (parse_options(init_str, &opts)) { return(1); } // Check that the target user for setuid is specified if current user is root if (opts.setuid_user == NULL) { LOG("Unable to setuid: specify a target username as the agent option user=<username>\n"); return(1); } // Check that this user exists if ((pwd = getpwnam(opts.setuid_user)) == NULL) { LOG("Unable to setuid: could not find user '%s'\n", opts.setuid_user); return(1); } // Boost the mlock limit up to infinity struct rlimit lim; lim.rlim_max = RLIM_INFINITY; lim.rlim_cur = lim.rlim_max; if (setrlimit(RLIMIT_MEMLOCK, &lim)) { perror(PREFIX "Unable to boost memlock resource limit"); warn_unless_root(); return(1); } // Actually lock our memory, including future allocations. if (mlockall(MCL_CURRENT | MCL_FUTURE)) { perror(PREFIX "Unable to lock memory"); warn_unless_root(); return(1); } // Drop down to the user's supplemental group list if (initgroups(opts.setuid_user, pwd->pw_gid)) { perror(PREFIX "Unable to initgroups"); warn_unless_root(); return(1); } // And primary group ID if (setgid(pwd->pw_gid)) { perror(PREFIX "Unable to setgid"); warn_unless_root(); return(1); } // And user ID if (setuid(pwd->pw_uid)) { perror(PREFIX "Unable to setuid"); warn_unless_root(); return(1); } LOG("Successfully locked memory and setuid to %s\n", opts.setuid_user); return(0); }
int main(int argc, char **argv) { int flag; int rc = 0; int index = 0; int argerr = 0; struct passwd *pwentry = NULL; crm_log_preinit(NULL, argc, argv); crm_set_options(NULL, "[options]", long_options, "Daemon for storing and replicating the cluster configuration"); crm_peer_init(); mainloop_add_signal(SIGTERM, cib_shutdown); mainloop_add_signal(SIGPIPE, cib_enable_writes); cib_writer = mainloop_add_trigger(G_PRIORITY_LOW, write_cib_contents, NULL); while (1) { flag = crm_get_option(argc, argv, &index); if (flag == -1) break; switch (flag) { case 'V': crm_bump_log_level(argc, argv); break; case 's': stand_alone = TRUE; preserve_status = TRUE; cib_writes_enabled = FALSE; pwentry = getpwnam(CRM_DAEMON_USER); CRM_CHECK(pwentry != NULL, crm_perror(LOG_ERR, "Invalid uid (%s) specified", CRM_DAEMON_USER); return CRM_EX_FATAL); rc = setgid(pwentry->pw_gid); if (rc < 0) { crm_perror(LOG_ERR, "Could not set group to %d", pwentry->pw_gid); return CRM_EX_FATAL; } rc = initgroups(CRM_DAEMON_USER, pwentry->pw_gid); if (rc < 0) { crm_perror(LOG_ERR, "Could not setup groups for user %d", pwentry->pw_uid); return CRM_EX_FATAL; } rc = setuid(pwentry->pw_uid); if (rc < 0) { crm_perror(LOG_ERR, "Could not set user to %d", pwentry->pw_uid); return CRM_EX_FATAL; } break; case '?': /* Help message */ crm_help(flag, CRM_EX_OK); break; case 'w': cib_writes_enabled = TRUE; break; case 'r': cib_root = optarg; break; case 'm': cib_metadata(); return CRM_EX_OK; default: ++argerr; break; } } if (argc - optind == 1 && safe_str_eq("metadata", argv[optind])) { cib_metadata(); return CRM_EX_OK; } if (optind > argc) { ++argerr; } if (argerr) { crm_help('?', CRM_EX_USAGE); } crm_log_init(NULL, LOG_INFO, TRUE, FALSE, argc, argv, FALSE); if (cib_root == NULL) { cib_root = CRM_CONFIG_DIR; } else { crm_notice("Using custom config location: %s", cib_root); } if (pcmk__daemon_can_write(cib_root, NULL) == FALSE) { crm_err("Terminating due to bad permissions on %s", cib_root); fprintf(stderr, "ERROR: Bad permissions on %s (see logs for details)\n", cib_root); fflush(stderr); return CRM_EX_FATAL; } /* read local config file */ cib_init(); // This should not be reachable CRM_CHECK(crm_hash_table_size(client_connections) == 0, crm_warn("Not all clients gone at exit")); g_hash_table_foreach(client_connections, log_cib_client, NULL); cib_cleanup(); crm_info("Done"); return CRM_EX_OK; }
void wbt_worker_process() { /* 设置进程标题 */ if( !wbt_conf.run_mode ) { wbt_set_proc_title("webit: worker process"); } /* 设置需要监听的信号(后台模式) */ struct sigaction act; sigset_t set; act.sa_sigaction = wbt_signal; sigemptyset(&act.sa_mask); act.sa_flags = SA_SIGINFO; sigemptyset(&set); sigaddset(&set, SIGCHLD); sigaddset(&set, SIGTERM); sigaddset(&set, SIGPIPE); if (sigprocmask(SIG_UNBLOCK, &set, NULL) == -1) { wbt_log_add("sigprocmask() failed\n"); } sigaction(SIGINT, &act, NULL); /* 退出信号 */ sigaction(SIGTERM, &act, NULL); /* 退出信号 */ sigaction(SIGPIPE, &act, NULL); /* 忽略 */ wbt_log_add("Webit startup (pid: %d)\n", getpid()); /* 降低 worker 进程的权限 */ const char * user = wbt_conf_get("user"); if ( user != NULL && geteuid() == 0 ) { // 根据用户名查询 // TODO getpwnam 应当在更早的时候调用。以免调用 getpwnam 失败的时候,工作进程被不断重启。 struct passwd * pw = getpwnam(user); if( pw == NULL ) { wbt_log_add("user %s not exists\n", user); return; } if (setgid(pw->pw_gid) == -1) { wbt_log_add("setgid(%d) failed", pw->pw_gid); return; } if (initgroups(user, pw->pw_gid) == -1) { wbt_log_add("initgroups(%s, %d) failed", user, pw->pw_gid); return; } if (setuid(pw->pw_uid) == -1) { wbt_log_add("setuid(%d) failed", pw->pw_uid); return; } } /* 进入事件循环 */ wbt_event_dispatch(); wbt_exit(0); }
static void do_spawn (void) { int n; int size; uid_t uid; gid_t gid; int env_inherit; pid_t child; int envs = 0; int log_stderr = 0; char *interpreter = NULL; char *log_file = NULL; char *uid_str = NULL; char *chroot_dir = NULL; char **envp = NULL; char *p = spawn_shared; const char *argv[] = {"sh", "-c", NULL, NULL}; #define CHECK_MARK(val) \ if ((*(int *)p) != val) { \ goto cleanup; \ } else { \ p += sizeof(int); \ } #define ALIGN4(buf) while ((long)p & 0x3) p++; /* Read the shared memory */ /* 1.- Interpreter */ CHECK_MARK (0xF0); size = *((int *)p); p += sizeof(int); if (size <= 0) { goto cleanup; } interpreter = malloc (sizeof("exec ") + size); if (interpreter == NULL) { goto cleanup; } strncpy (interpreter, "exec ", 5); strncpy (interpreter + 5, p, size + 1); p += size + 1; ALIGN4 (p); /* 2.- UID & GID */ CHECK_MARK (0xF1); size = *((int *)p); if (size > 0) { uid_str = strdup (p + sizeof(int)); } p += sizeof(int) + size + 1; ALIGN4 (p); memcpy (&uid, p, sizeof(uid_t)); p += sizeof(uid_t); memcpy (&gid, p, sizeof(gid_t)); p += sizeof(gid_t); /* 3.- Chroot directory */ CHECK_MARK (0xF2); size = *((int *) p); p += sizeof(int); if (size > 0) { chroot_dir = malloc(size + 1); memcpy(chroot_dir, p, size + 1); } p += size + 1; ALIGN4 (p); /* 4.- Environment */ CHECK_MARK (0xF3); env_inherit = *((int *)p); p += sizeof(int); envs = *((int *)p); p += sizeof(int); envp = malloc (sizeof(char *) * (envs + 1)); if (envp == NULL) { goto cleanup; } envp[envs] = NULL; for (n=0; n<envs; n++) { char *e; size = *((int *)p); p += sizeof(int); e = malloc (size + 1); if (e == NULL) { goto cleanup; } memcpy (e, p, size); e[size] = '\0'; envp[n] = e; p += size + 1; ALIGN4 (p); } /* 5.- Error log */ CHECK_MARK (0xF4); size = *((int *)p); p += sizeof(int); if (size > 0) { if (! strncmp (p, "stderr", 6)) { log_stderr = 1; } else if (! strncmp (p, "file,", 5)) { log_file = p+5; } p += (size + 1); ALIGN4 (p); } /* 6.- PID: it's -1 now */ CHECK_MARK (0xF5); n = *((int *)p); if (n > 0) { kill (n, SIGTERM); *p = -1; } /* Spawn */ child = fork(); switch (child) { case 0: { int i; struct sigaction sig_action; /* Reset signal handlers */ sig_action.sa_handler = SIG_DFL; sig_action.sa_flags = 0; sigemptyset (&sig_action.sa_mask); for (i=0 ; i < NSIG ; i++) { sigaction (i, &sig_action, NULL); } /* Logging */ if (log_file) { int fd; fd = open (log_file, O_WRONLY | O_APPEND | O_CREAT, 0600); if (fd < 0) { PRINT_ERROR ("(warning) Couldn't open '%s' for writing..\n", log_file); } close (STDOUT_FILENO); close (STDERR_FILENO); dup2 (fd, STDOUT_FILENO); dup2 (fd, STDERR_FILENO); } else if (log_stderr) { /* do nothing */ } else { int tmp_fd; tmp_fd = open ("/dev/null", O_WRONLY); close (STDOUT_FILENO); close (STDERR_FILENO); dup2 (tmp_fd, STDOUT_FILENO); dup2 (tmp_fd, STDERR_FILENO); } /* Change root */ if (chroot_dir) { int re = chroot(chroot_dir); if (re < 0) { PRINT_ERROR ("(critial) Couldn't chroot to %s\n", chroot_dir); exit (1); } } /* Change user & group */ if (uid_str != NULL) { n = initgroups (uid_str, gid); if (n == -1) { PRINT_ERROR ("(warning) initgroups failed User=%s, GID=%d\n", uid_str, gid); } } if ((int)gid != -1) { n = setgid (gid); if (n != 0) { PRINT_ERROR ("(warning) Couldn't set GID=%d\n", gid); } } if ((int)uid != -1) { n = setuid (uid); if (n != 0) { PRINT_ERROR ("(warning) Couldn't set UID=%d\n", uid); } } /* Clean the shared memory */ size = (p - spawn_shared) - sizeof(int); memset (spawn_shared, 0, size); /* Execute the interpreter */ argv[2] = interpreter; if (env_inherit) { do { execv ("/bin/sh", (char **)argv); } while (errno == EINTR); } else { do { execve ("/bin/sh", (char **)argv, envp); } while (errno == EINTR); } PRINT_MSG ("(critical) Couldn't spawn: sh -c %s\n", interpreter); exit (1); } case -1: /* Error */ PRINT_MSG ("(critical) Couldn't fork(): %s\n", strerror(errno)); goto cleanup; default: break; } /* Return the PID */ memcpy (p, (char *)&child, sizeof(int)); printf ("PID %d: launched '/bin/sh -c %s' with uid=%d, gid=%d, chroot=%s, env=%s\n", child, interpreter, uid, gid, chroot_dir, env_inherit ? "inherited":"custom"); cleanup: /* Unlock worker */ do_sem_op (SEM_LAUNCH_READY, 1); /* Clean up */ free (uid_str); free (interpreter); free (chroot_dir); if (envp != NULL) { for (n=0; n<envs; n++) { free (envp[n]); } free (envp); } }
/*[p]工作进程初始化*/ static void ngx_worker_process_init(ngx_cycle_t *cycle, ngx_uint_t priority) { sigset_t set; ngx_int_t n; ngx_uint_t i; struct rlimit rlmt; ngx_core_conf_t *ccf; ngx_listening_t *ls; //[p] 设置nginx服务器的运行环境 if (ngx_set_environment(cycle, NULL) == NULL) { /* fatal */ exit(2); } //[p]获取配置的上下文 ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); if (priority && ccf->priority != 0) { if (setpriority(PRIO_PROCESS, 0, ccf->priority) == -1) { //[p] 设置工作进程优先级 ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, //[p]出错处理 "setpriority(%d) failed", ccf->priority); } } if (ccf->rlimit_nofile != NGX_CONF_UNSET) { rlmt.rlim_cur = (rlim_t) ccf->rlimit_nofile; //[p] 获取配置信息 rlmt.rlim_max = (rlim_t) ccf->rlimit_nofile; if (setrlimit(RLIMIT_NOFILE, &rlmt) == -1) { //[p]设置打开文件描述符的上限 ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "setrlimit(RLIMIT_NOFILE, %i) failed", ccf->rlimit_nofile); } } if (ccf->rlimit_core != NGX_CONF_UNSET) { rlmt.rlim_cur = (rlim_t) ccf->rlimit_core; rlmt.rlim_max = (rlim_t) ccf->rlimit_core; if (setrlimit(RLIMIT_CORE, &rlmt) == -1) { //[p] 设置内核转储文件的最大长度 ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "setrlimit(RLIMIT_CORE, %O) failed", ccf->rlimit_core); } } #ifdef RLIMIT_SIGPENDING if (ccf->rlimit_sigpending != NGX_CONF_UNSET) { rlmt.rlim_cur = (rlim_t) ccf->rlimit_sigpending; rlmt.rlim_max = (rlim_t) ccf->rlimit_sigpending; if (setrlimit(RLIMIT_SIGPENDING, &rlmt) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "setrlimit(RLIMIT_SIGPENDING, %i) failed", ccf->rlimit_sigpending); } } #endif if (geteuid() == 0) { if (setgid(ccf->group) == -1) { //[p]设置组ID ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, "setgid(%d) failed", ccf->group); /* fatal */ exit(2); } if (initgroups(ccf->username, ccf->group) == -1) { //[p] 初始化组清单 ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, "initgroups(%s, %d) failed", ccf->username, ccf->group); } if (setuid(ccf->user) == -1) { //[p] 设置用户ID ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, "setuid(%d) failed", ccf->user); /* fatal */ exit(2); } } #if (NGX_HAVE_SCHED_SETAFFINITY) if (cpu_affinity) { ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "sched_setaffinity(0x%08Xl)", cpu_affinity); if (sched_setaffinity(0, sizeof(cpu_affinity), (cpu_set_t *) &cpu_affinity) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "sched_setaffinity(0x%08Xl) failed", cpu_affinity); } } #endif #if (NGX_HAVE_PR_SET_DUMPABLE) /* allow coredump after setuid() in Linux 2.4.x */ if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "prctl(PR_SET_DUMPABLE) failed"); } #endif if (ccf->working_directory.len) { if (chdir((char *) ccf->working_directory.data) == -1) { //[p]设置进程的工作目录 ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "chdir(\"%s\") failed", ccf->working_directory.data); /* fatal */ exit(2); } } sigemptyset(&set); if (sigprocmask(SIG_SETMASK, &set, NULL) == -1) { //[p] 取消最信号的屏蔽 ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "sigprocmask() failed"); } /* * disable deleting previous events for the listening sockets because * in the worker processes there are no events at all at this point */ ls = cycle->listening.elts; for (i = 0; i < cycle->listening.nelts; i++) { ls[i].previous = NULL; //[p] 遍历并设置所有监听套接字的状态 } //[p] 初始化nginx各个模块 for (i = 0; ngx_modules[i]; i++) { if (ngx_modules[i]->init_process) { //进程初始化, 调用每个模块的init_process,用它做模块开发的时候,使用得挺少的 //这里要特别看的是event模块: //nginx的event模块包含一个init_process,也就是ngx_event_process_init(ngx_event.c). //这个函数就是nginx的驱动器,他初始化事件驱动器,连接池,定时器,以及挂在listen 句柄的回调函数 if (ngx_modules[i]->init_process(cycle) == NGX_ERROR) { /* fatal */ exit(2); } } } // 这里循环用户关闭其他worker进程的无用channel资源 for (n = 0; n < ngx_last_process; n++) { if (ngx_processes[n].pid == -1) { //n位置的进程不存在,这里是预防性的代码 continue; } //ngx_process_slot是创建worker进程的时候,从master进程复制过来的,此处就是指本worker进程在数组中的索引位置 if (n == ngx_process_slot) { continue; } if (ngx_processes[n].channel[1] == -1) { // channel不存在,跳过 continue; } // 创建worker进程时,会将master的资源复制过来,因此需要关闭无用的channel -- 其他worker进程的读端描述符 if (close(ngx_processes[n].channel[1]) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "close() channel failed"); } } // 关闭本worker进程的channel的写端描述符。 if (close(ngx_processes[ngx_process_slot].channel[0]) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "close() channel failed"); } #if 0 ngx_last_process = 0; #endif // 将channel放入nginx关心的集合中,同时关注channel上的读事件。 //[p] 设置当前工作进程从channel[1]中监听事件 if (ngx_add_channel_event(cycle, ngx_channel, NGX_READ_EVENT, ngx_channel_handler) == NGX_ERROR) { /* fatal */ exit(2); } }
int main(int argc, char *argv[]) { /* Scratch variables... */ int c; pid_t oldpid; size_t i; struct sigaction action; #ifdef HAVE_GETPWNAM struct passwd *pwd = NULL; #endif /* HAVE_GETPWNAM */ struct addrinfo hints[2]; int hints_in_use = 1; char** nodes = NULL; /* array of address strings, size nsd.ifs */ const char *udp_port = 0; const char *tcp_port = 0; const char *configfile = CONFIGFILE; char* argv0 = (argv0 = strrchr(argv[0], '/')) ? argv0 + 1 : argv[0]; log_init(argv0); /* Initialize the server handler... */ memset(&nsd, 0, sizeof(struct nsd)); nsd.region = region_create(xalloc, free); nsd.dbfile = 0; nsd.pidfile = 0; nsd.server_kind = NSD_SERVER_MAIN; memset(&hints, 0, sizeof(*hints)*2); hints[0].ai_family = DEFAULT_AI_FAMILY; hints[0].ai_flags = AI_PASSIVE; hints[1].ai_family = DEFAULT_AI_FAMILY; hints[1].ai_flags = AI_PASSIVE; nsd.identity = 0; nsd.version = VERSION; nsd.username = 0; nsd.chrootdir = 0; nsd.nsid = NULL; nsd.nsid_len = 0; nsd.child_count = 0; nsd.maximum_tcp_count = 0; nsd.current_tcp_count = 0; nsd.grab_ip6_optional = 0; nsd.file_rotation_ok = 0; /* Set up our default identity to gethostname(2) */ if (gethostname(hostname, MAXHOSTNAMELEN) == 0) { nsd.identity = hostname; } else { log_msg(LOG_ERR, "failed to get the host name: %s - using default identity", strerror(errno)); nsd.identity = IDENTITY; } /* Parse the command line... */ while ((c = getopt(argc, argv, "46a:c:df:hi:I:l:N:n:P:p:s:u:t:X:V:v" #ifndef NDEBUG /* <mattthijs> only when configured with --enable-checking */ "F:L:" #endif /* NDEBUG */ )) != -1) { switch (c) { case '4': hints[0].ai_family = AF_INET; break; case '6': #ifdef INET6 hints[0].ai_family = AF_INET6; #else /* !INET6 */ error("IPv6 support not enabled."); #endif /* INET6 */ break; case 'a': add_interface(&nodes, &nsd, optarg); break; case 'c': configfile = optarg; break; case 'd': nsd.debug = 1; break; case 'f': nsd.dbfile = optarg; break; case 'h': usage(); exit(0); case 'i': nsd.identity = optarg; break; case 'I': if (nsd.nsid_len != 0) { /* can only be given once */ break; } if (strncasecmp(optarg, "ascii_", 6) == 0) { nsd.nsid = xalloc(strlen(optarg+6)); nsd.nsid_len = strlen(optarg+6); memmove(nsd.nsid, optarg+6, nsd.nsid_len); } else { if (strlen(optarg) % 2 != 0) { error("the NSID must be a hex string of an even length."); } nsd.nsid = xalloc(strlen(optarg) / 2); nsd.nsid_len = strlen(optarg) / 2; if (hex_pton(optarg, nsd.nsid, nsd.nsid_len) == -1) { error("hex string cannot be parsed '%s' in NSID.", optarg); } } break; case 'l': nsd.log_filename = optarg; break; case 'N': i = atoi(optarg); if (i <= 0) { error("number of child servers must be greater than zero."); } else { nsd.child_count = i; } break; case 'n': i = atoi(optarg); if (i <= 0) { error("number of concurrent TCP connections must greater than zero."); } else { nsd.maximum_tcp_count = i; } break; case 'P': nsd.pidfile = optarg; break; case 'p': if (atoi(optarg) == 0) { error("port argument must be numeric."); } tcp_port = optarg; udp_port = optarg; break; case 's': #ifdef BIND8_STATS nsd.st.period = atoi(optarg); #else /* !BIND8_STATS */ error("BIND 8 statistics not enabled."); #endif /* BIND8_STATS */ break; case 't': #ifdef HAVE_CHROOT nsd.chrootdir = optarg; #else /* !HAVE_CHROOT */ error("chroot not supported on this platform."); #endif /* HAVE_CHROOT */ break; case 'u': nsd.username = optarg; break; case 'V': verbosity = atoi(optarg); break; case 'v': version(); /* version exits */ break; #ifndef NDEBUG case 'F': sscanf(optarg, "%x", &nsd_debug_facilities); break; case 'L': sscanf(optarg, "%d", &nsd_debug_level); break; #endif /* NDEBUG */ case '?': default: usage(); exit(1); } } argc -= optind; /* argv += optind; */ /* Commandline parse error */ if (argc != 0) { usage(); exit(1); } if (strlen(nsd.identity) > UCHAR_MAX) { error("server identity too long (%u characters)", (unsigned) strlen(nsd.identity)); } if(!tsig_init(nsd.region)) error("init tsig failed"); /* Read options */ nsd.options = nsd_options_create(region_create_custom(xalloc, free, DEFAULT_CHUNK_SIZE, DEFAULT_LARGE_OBJECT_SIZE, DEFAULT_INITIAL_CLEANUP_SIZE, 1)); if(!parse_options_file(nsd.options, configfile, NULL, NULL)) { error("could not read config: %s\n", configfile); } if(!parse_zone_list_file(nsd.options)) { error("could not read zonelist file %s\n", nsd.options->zonelistfile); } if(nsd.options->do_ip4 && !nsd.options->do_ip6) { hints[0].ai_family = AF_INET; } #ifdef INET6 if(nsd.options->do_ip6 && !nsd.options->do_ip4) { hints[0].ai_family = AF_INET6; } #endif /* INET6 */ if(nsd.options->ip_addresses) { ip_address_option_type* ip = nsd.options->ip_addresses; while(ip) { add_interface(&nodes, &nsd, ip->address); ip = ip->next; } } if (verbosity == 0) verbosity = nsd.options->verbosity; #ifndef NDEBUG if (nsd_debug_level > 0 && verbosity == 0) verbosity = nsd_debug_level; #endif /* NDEBUG */ if(nsd.options->debug_mode) nsd.debug=1; if(!nsd.dbfile) { if(nsd.options->database) nsd.dbfile = nsd.options->database; else nsd.dbfile = DBFILE; } if(!nsd.pidfile) { if(nsd.options->pidfile) nsd.pidfile = nsd.options->pidfile; else nsd.pidfile = PIDFILE; } if(strcmp(nsd.identity, hostname)==0 || strcmp(nsd.identity,IDENTITY)==0) { if(nsd.options->identity) nsd.identity = nsd.options->identity; } if(nsd.options->version) { nsd.version = nsd.options->version; } if (nsd.options->logfile && !nsd.log_filename) { nsd.log_filename = nsd.options->logfile; } if(nsd.child_count == 0) { nsd.child_count = nsd.options->server_count; } #ifdef SO_REUSEPORT if(nsd.options->reuseport && nsd.child_count > 1) { nsd.reuseport = nsd.child_count; } #endif /* SO_REUSEPORT */ if(nsd.maximum_tcp_count == 0) { nsd.maximum_tcp_count = nsd.options->tcp_count; } nsd.tcp_timeout = nsd.options->tcp_timeout; nsd.tcp_query_count = nsd.options->tcp_query_count; nsd.tcp_mss = nsd.options->tcp_mss; nsd.outgoing_tcp_mss = nsd.options->outgoing_tcp_mss; nsd.ipv4_edns_size = nsd.options->ipv4_edns_size; nsd.ipv6_edns_size = nsd.options->ipv6_edns_size; if(udp_port == 0) { if(nsd.options->port != 0) { udp_port = nsd.options->port; tcp_port = nsd.options->port; } else { udp_port = UDP_PORT; tcp_port = TCP_PORT; } } #ifdef BIND8_STATS if(nsd.st.period == 0) { nsd.st.period = nsd.options->statistics; } #endif /* BIND8_STATS */ #ifdef HAVE_CHROOT if(nsd.chrootdir == 0) nsd.chrootdir = nsd.options->chroot; #ifdef CHROOTDIR /* if still no chrootdir, fallback to default */ if(nsd.chrootdir == 0) nsd.chrootdir = CHROOTDIR; #endif /* CHROOTDIR */ #endif /* HAVE_CHROOT */ if(nsd.username == 0) { if(nsd.options->username) nsd.username = nsd.options->username; else nsd.username = USER; } if(nsd.options->zonesdir && nsd.options->zonesdir[0]) { if(chdir(nsd.options->zonesdir)) { error("cannot chdir to '%s': %s", nsd.options->zonesdir, strerror(errno)); } DEBUG(DEBUG_IPC,1, (LOG_INFO, "changed directory to %s", nsd.options->zonesdir)); } /* EDNS0 */ edns_init_data(&nsd.edns_ipv4, nsd.options->ipv4_edns_size); #if defined(INET6) #if defined(IPV6_USE_MIN_MTU) || defined(IPV6_MTU) edns_init_data(&nsd.edns_ipv6, nsd.options->ipv6_edns_size); #else /* no way to set IPV6 MTU, send no bigger than that. */ if (nsd.options->ipv6_edns_size < IPV6_MIN_MTU) edns_init_data(&nsd.edns_ipv6, nsd.options->ipv6_edns_size); else edns_init_data(&nsd.edns_ipv6, IPV6_MIN_MTU); #endif /* IPV6 MTU) */ #endif /* defined(INET6) */ if (nsd.nsid_len == 0 && nsd.options->nsid) { if (strlen(nsd.options->nsid) % 2 != 0) { error("the NSID must be a hex string of an even length."); } nsd.nsid = xalloc(strlen(nsd.options->nsid) / 2); nsd.nsid_len = strlen(nsd.options->nsid) / 2; if (hex_pton(nsd.options->nsid, nsd.nsid, nsd.nsid_len) == -1) { error("hex string cannot be parsed '%s' in NSID.", nsd.options->nsid); } } edns_init_nsid(&nsd.edns_ipv4, nsd.nsid_len); #if defined(INET6) edns_init_nsid(&nsd.edns_ipv6, nsd.nsid_len); #endif /* defined(INET6) */ /* Number of child servers to fork. */ nsd.children = (struct nsd_child *) region_alloc_array( nsd.region, nsd.child_count, sizeof(struct nsd_child)); for (i = 0; i < nsd.child_count; ++i) { nsd.children[i].kind = NSD_SERVER_BOTH; nsd.children[i].pid = -1; nsd.children[i].child_fd = -1; nsd.children[i].parent_fd = -1; nsd.children[i].handler = NULL; nsd.children[i].need_to_send_STATS = 0; nsd.children[i].need_to_send_QUIT = 0; nsd.children[i].need_to_exit = 0; nsd.children[i].has_exited = 0; #ifdef BIND8_STATS nsd.children[i].query_count = 0; #endif } nsd.this_child = NULL; /* We need at least one active interface */ if (nsd.ifs == 0) { add_interface(&nodes, &nsd, NULL); /* * With IPv6 we'd like to open two separate sockets, * one for IPv4 and one for IPv6, both listening to * the wildcard address (unless the -4 or -6 flags are * specified). * * However, this is only supported on platforms where * we can turn the socket option IPV6_V6ONLY _on_. * Otherwise we just listen to a single IPv6 socket * and any incoming IPv4 connections will be * automatically mapped to our IPv6 socket. */ #ifdef INET6 if (hints[0].ai_family == AF_UNSPEC) { #ifdef IPV6_V6ONLY add_interface(&nodes, &nsd, NULL); hints[0].ai_family = AF_INET6; hints[1].ai_family = AF_INET; hints_in_use = 2; nsd.grab_ip6_optional = 1; #else /* !IPV6_V6ONLY */ hints[0].ai_family = AF_INET6; #endif /* IPV6_V6ONLY */ } #endif /* INET6 */ } /* Set up the address info structures with real interface/port data */ assert(nodes); for (i = 0; i < nsd.ifs; ++i) { int r; const char* node = NULL; const char* service = NULL; int h = ((hints_in_use == 1)?0:i%hints_in_use); /* We don't perform name-lookups */ if (nodes[i] != NULL) hints[h].ai_flags |= AI_NUMERICHOST; get_ip_port_frm_str(nodes[i], &node, &service); hints[h].ai_socktype = SOCK_DGRAM; if ((r=getaddrinfo(node, (service?service:udp_port), &hints[h], &nsd.udp[i].addr)) != 0) { #ifdef INET6 if(nsd.grab_ip6_optional && hints[0].ai_family == AF_INET6) { log_msg(LOG_WARNING, "No IPv6, fallback to IPv4. getaddrinfo: %s", r==EAI_SYSTEM?strerror(errno):gai_strerror(r)); continue; } #endif error("cannot parse address '%s': getaddrinfo: %s %s", nodes[i]?nodes[i]:"(null)", gai_strerror(r), r==EAI_SYSTEM?strerror(errno):""); } hints[h].ai_socktype = SOCK_STREAM; if ((r=getaddrinfo(node, (service?service:tcp_port), &hints[h], &nsd.tcp[i].addr)) != 0) { error("cannot parse address '%s': getaddrinfo: %s %s", nodes[i]?nodes[i]:"(null)", gai_strerror(r), r==EAI_SYSTEM?strerror(errno):""); } } /* Parse the username into uid and gid */ nsd.gid = getgid(); nsd.uid = getuid(); #ifdef HAVE_GETPWNAM /* Parse the username into uid and gid */ if (*nsd.username) { if (isdigit((unsigned char)*nsd.username)) { char *t; nsd.uid = strtol(nsd.username, &t, 10); if (*t != 0) { if (*t != '.' || !isdigit((unsigned char)*++t)) { error("-u user or -u uid or -u uid.gid"); } nsd.gid = strtol(t, &t, 10); } else { /* Lookup the group id in /etc/passwd */ if ((pwd = getpwuid(nsd.uid)) == NULL) { error("user id %u does not exist.", (unsigned) nsd.uid); } else { nsd.gid = pwd->pw_gid; } } } else { /* Lookup the user id in /etc/passwd */ if ((pwd = getpwnam(nsd.username)) == NULL) { error("user '%s' does not exist.", nsd.username); } else { nsd.uid = pwd->pw_uid; nsd.gid = pwd->pw_gid; } } } /* endpwent(); */ #endif /* HAVE_GETPWNAM */ #if defined(HAVE_SSL) key_options_tsig_add(nsd.options); #endif append_trailing_slash(&nsd.options->xfrdir, nsd.options->region); /* Check relativity of pathnames to chroot */ if (nsd.chrootdir && nsd.chrootdir[0]) { /* existing chrootdir: append trailing slash for strncmp checking */ append_trailing_slash(&nsd.chrootdir, nsd.region); append_trailing_slash(&nsd.options->zonesdir, nsd.options->region); /* zonesdir must be absolute and within chroot, * all other pathnames may be relative to zonesdir */ if (strncmp(nsd.options->zonesdir, nsd.chrootdir, strlen(nsd.chrootdir)) != 0) { error("zonesdir %s has to be an absolute path that starts with the chroot path %s", nsd.options->zonesdir, nsd.chrootdir); } else if (!file_inside_chroot(nsd.pidfile, nsd.chrootdir)) { error("pidfile %s is not relative to %s: chroot not possible", nsd.pidfile, nsd.chrootdir); } else if (!file_inside_chroot(nsd.dbfile, nsd.chrootdir)) { error("database %s is not relative to %s: chroot not possible", nsd.dbfile, nsd.chrootdir); } else if (!file_inside_chroot(nsd.options->xfrdfile, nsd.chrootdir)) { error("xfrdfile %s is not relative to %s: chroot not possible", nsd.options->xfrdfile, nsd.chrootdir); } else if (!file_inside_chroot(nsd.options->zonelistfile, nsd.chrootdir)) { error("zonelistfile %s is not relative to %s: chroot not possible", nsd.options->zonelistfile, nsd.chrootdir); } else if (!file_inside_chroot(nsd.options->xfrdir, nsd.chrootdir)) { error("xfrdir %s is not relative to %s: chroot not possible", nsd.options->xfrdir, nsd.chrootdir); } } /* Set up the logging */ log_open(LOG_PID, FACILITY, nsd.log_filename); if (!nsd.log_filename) log_set_log_function(log_syslog); else if (nsd.uid && nsd.gid) { if(chown(nsd.log_filename, nsd.uid, nsd.gid) != 0) VERBOSITY(2, (LOG_WARNING, "chown %s failed: %s", nsd.log_filename, strerror(errno))); } log_msg(LOG_NOTICE, "%s starting (%s)", argv0, PACKAGE_STRING); /* Do we have a running nsd? */ if ((oldpid = readpid(nsd.pidfile)) == -1) { if (errno != ENOENT) { log_msg(LOG_ERR, "can't read pidfile %s: %s", nsd.pidfile, strerror(errno)); } } else { if (kill(oldpid, 0) == 0 || errno == EPERM) { log_msg(LOG_WARNING, "%s is already running as %u, continuing", argv0, (unsigned) oldpid); } else { log_msg(LOG_ERR, "...stale pid file from process %u", (unsigned) oldpid); } } /* Setup the signal handling... */ action.sa_handler = sig_handler; sigfillset(&action.sa_mask); action.sa_flags = 0; sigaction(SIGTERM, &action, NULL); sigaction(SIGHUP, &action, NULL); sigaction(SIGINT, &action, NULL); sigaction(SIGILL, &action, NULL); sigaction(SIGUSR1, &action, NULL); sigaction(SIGALRM, &action, NULL); sigaction(SIGCHLD, &action, NULL); action.sa_handler = SIG_IGN; sigaction(SIGPIPE, &action, NULL); /* Initialize... */ nsd.mode = NSD_RUN; nsd.signal_hint_child = 0; nsd.signal_hint_reload = 0; nsd.signal_hint_reload_hup = 0; nsd.signal_hint_quit = 0; nsd.signal_hint_shutdown = 0; nsd.signal_hint_stats = 0; nsd.signal_hint_statsusr = 0; nsd.quit_sync_done = 0; /* Initialize the server... */ if (server_init(&nsd) != 0) { error("server initialization failed, %s could " "not be started", argv0); } #if defined(HAVE_SSL) if(nsd.options->control_enable) { /* read ssl keys while superuser and outside chroot */ if(!(nsd.rc = daemon_remote_create(nsd.options))) error("could not perform remote control setup"); } #endif /* HAVE_SSL */ /* Unless we're debugging, fork... */ if (!nsd.debug) { int fd; /* Take off... */ switch ((nsd.pid = fork())) { case 0: /* Child */ break; case -1: error("fork() failed: %s", strerror(errno)); break; default: /* Parent is done */ server_close_all_sockets(nsd.udp, nsd.ifs); server_close_all_sockets(nsd.tcp, nsd.ifs); exit(0); } /* Detach ourselves... */ if (setsid() == -1) { error("setsid() failed: %s", strerror(errno)); } if ((fd = open("/dev/null", O_RDWR, 0)) != -1) { (void)dup2(fd, STDIN_FILENO); (void)dup2(fd, STDOUT_FILENO); (void)dup2(fd, STDERR_FILENO); if (fd > 2) (void)close(fd); } } /* Get our process id */ nsd.pid = getpid(); /* Set user context */ #ifdef HAVE_GETPWNAM if (*nsd.username) { #ifdef HAVE_SETUSERCONTEXT /* setusercontext does initgroups, setuid, setgid, and * also resource limits from login config, but we * still call setresuid, setresgid to be sure to set all uid */ if (setusercontext(NULL, pwd, nsd.uid, LOGIN_SETALL & ~LOGIN_SETUSER & ~LOGIN_SETGROUP) != 0) log_msg(LOG_WARNING, "unable to setusercontext %s: %s", nsd.username, strerror(errno)); #endif /* HAVE_SETUSERCONTEXT */ } #endif /* HAVE_GETPWNAM */ /* Chroot */ #ifdef HAVE_CHROOT if (nsd.chrootdir && nsd.chrootdir[0]) { int l = strlen(nsd.chrootdir)-1; /* ends in trailing slash */ if (file_inside_chroot(nsd.log_filename, nsd.chrootdir)) nsd.file_rotation_ok = 1; /* strip chroot from pathnames if they're absolute */ nsd.options->zonesdir += l; if (nsd.log_filename){ if (nsd.log_filename[0] == '/') nsd.log_filename += l; } if (nsd.pidfile[0] == '/') nsd.pidfile += l; if (nsd.dbfile[0] == '/') nsd.dbfile += l; if (nsd.options->xfrdfile[0] == '/') nsd.options->xfrdfile += l; if (nsd.options->zonelistfile[0] == '/') nsd.options->zonelistfile += l; if (nsd.options->xfrdir[0] == '/') nsd.options->xfrdir += l; /* strip chroot from pathnames of "include:" statements * on subsequent repattern commands */ cfg_parser->chroot = nsd.chrootdir; #ifdef HAVE_TZSET /* set timezone whilst not yet in chroot */ tzset(); #endif if (chroot(nsd.chrootdir)) { error("unable to chroot: %s", strerror(errno)); } if (chdir("/")) { error("unable to chdir to chroot: %s", strerror(errno)); } DEBUG(DEBUG_IPC,1, (LOG_INFO, "changed root directory to %s", nsd.chrootdir)); /* chdir to zonesdir again after chroot */ if(nsd.options->zonesdir && nsd.options->zonesdir[0]) { if(chdir(nsd.options->zonesdir)) { error("unable to chdir to '%s': %s", nsd.options->zonesdir, strerror(errno)); } DEBUG(DEBUG_IPC,1, (LOG_INFO, "changed directory to %s", nsd.options->zonesdir)); } } else #endif /* HAVE_CHROOT */ nsd.file_rotation_ok = 1; DEBUG(DEBUG_IPC,1, (LOG_INFO, "file rotation on %s %sabled", nsd.log_filename, nsd.file_rotation_ok?"en":"dis")); /* Write pidfile */ if (writepid(&nsd) == -1) { log_msg(LOG_ERR, "cannot overwrite the pidfile %s: %s", nsd.pidfile, strerror(errno)); } /* Drop the permissions */ #ifdef HAVE_GETPWNAM if (*nsd.username) { #ifdef HAVE_INITGROUPS if(initgroups(nsd.username, nsd.gid) != 0) log_msg(LOG_WARNING, "unable to initgroups %s: %s", nsd.username, strerror(errno)); #endif /* HAVE_INITGROUPS */ endpwent(); #ifdef HAVE_SETRESGID if(setresgid(nsd.gid,nsd.gid,nsd.gid) != 0) #elif defined(HAVE_SETREGID) && !defined(DARWIN_BROKEN_SETREUID) if(setregid(nsd.gid,nsd.gid) != 0) #else /* use setgid */ if(setgid(nsd.gid) != 0) #endif /* HAVE_SETRESGID */ error("unable to set group id of %s: %s", nsd.username, strerror(errno)); #ifdef HAVE_SETRESUID if(setresuid(nsd.uid,nsd.uid,nsd.uid) != 0) #elif defined(HAVE_SETREUID) && !defined(DARWIN_BROKEN_SETREUID) if(setreuid(nsd.uid,nsd.uid) != 0) #else /* use setuid */ if(setuid(nsd.uid) != 0) #endif /* HAVE_SETRESUID */ error("unable to set user id of %s: %s", nsd.username, strerror(errno)); DEBUG(DEBUG_IPC,1, (LOG_INFO, "dropped user privileges, run as %s", nsd.username)); } #endif /* HAVE_GETPWNAM */ xfrd_make_tempdir(&nsd); #ifdef USE_ZONE_STATS options_zonestatnames_create(nsd.options); server_zonestat_alloc(&nsd); #endif /* USE_ZONE_STATS */ #ifdef USE_DNSTAP if(nsd.options->dnstap_enable) { nsd.dt_collector = dt_collector_create(&nsd); dt_collector_start(nsd.dt_collector, &nsd); } #endif /* USE_DNSTAP */ if(nsd.server_kind == NSD_SERVER_MAIN) { server_prepare_xfrd(&nsd); /* xfrd forks this before reading database, so it does not get * the memory size of the database */ server_start_xfrd(&nsd, 0, 0); /* close zonelistfile in non-xfrd processes */ zone_list_close(nsd.options); } if (server_prepare(&nsd) != 0) { unlinkpid(nsd.pidfile); error("server preparation failed, %s could " "not be started", argv0); } if(nsd.server_kind == NSD_SERVER_MAIN) { server_send_soa_xfrd(&nsd, 0); } /* Really take off */ log_msg(LOG_NOTICE, "%s started (%s), pid %d", argv0, PACKAGE_STRING, (int) nsd.pid); if (nsd.server_kind == NSD_SERVER_MAIN) { server_main(&nsd); } else { server_child(&nsd); } /* NOTREACH */ exit(0); }
static void ngx_worker_process_init(ngx_cycle_t *cycle, ngx_int_t worker) { sigset_t set; uint64_t cpu_affinity; ngx_int_t n; ngx_uint_t i; struct rlimit rlmt; ngx_core_conf_t *ccf; ngx_listening_t *ls; if (ngx_set_environment(cycle, NULL) == NULL) { /* fatal */ exit(2); } ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); if (worker >= 0 && ccf->priority != 0) { if (setpriority(PRIO_PROCESS, 0, ccf->priority) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "setpriority(%d) failed", ccf->priority); } } if (ccf->rlimit_nofile != NGX_CONF_UNSET) { rlmt.rlim_cur = (rlim_t) ccf->rlimit_nofile; rlmt.rlim_max = (rlim_t) ccf->rlimit_nofile; if (setrlimit(RLIMIT_NOFILE, &rlmt) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "setrlimit(RLIMIT_NOFILE, %i) failed", ccf->rlimit_nofile); } } if (ccf->rlimit_core != NGX_CONF_UNSET) { rlmt.rlim_cur = (rlim_t) ccf->rlimit_core; rlmt.rlim_max = (rlim_t) ccf->rlimit_core; if (setrlimit(RLIMIT_CORE, &rlmt) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "setrlimit(RLIMIT_CORE, %O) failed", ccf->rlimit_core); } } #ifdef RLIMIT_SIGPENDING if (ccf->rlimit_sigpending != NGX_CONF_UNSET) { rlmt.rlim_cur = (rlim_t) ccf->rlimit_sigpending; rlmt.rlim_max = (rlim_t) ccf->rlimit_sigpending; if (setrlimit(RLIMIT_SIGPENDING, &rlmt) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "setrlimit(RLIMIT_SIGPENDING, %i) failed", ccf->rlimit_sigpending); } } #endif if (geteuid() == 0) { if (setgid(ccf->group) == -1) { ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, "setgid(%d) failed", ccf->group); /* fatal */ exit(2); } if (initgroups(ccf->username, ccf->group) == -1) { ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, "initgroups(%s, %d) failed", ccf->username, ccf->group); } if (setuid(ccf->user) == -1) { ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, "setuid(%d) failed", ccf->user); /* fatal */ exit(2); } } if (worker >= 0) { cpu_affinity = ngx_get_cpu_affinity(worker); if (cpu_affinity) { ngx_setaffinity(cpu_affinity, cycle->log); } } #if (NGX_HAVE_PR_SET_DUMPABLE) /* allow coredump after setuid() in Linux 2.4.x */ if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "prctl(PR_SET_DUMPABLE) failed"); } #endif if (ccf->working_directory.len) { if (chdir((char *) ccf->working_directory.data) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "chdir(\"%s\") failed", ccf->working_directory.data); /* fatal */ exit(2); } } sigemptyset(&set); if (sigprocmask(SIG_SETMASK, &set, NULL) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "sigprocmask() failed"); } srandom((ngx_pid << 16) ^ ngx_time()); /* * disable deleting previous events for the listening sockets because * in the worker processes there are no events at all at this point */ ls = cycle->listening.elts; for (i = 0; i < cycle->listening.nelts; i++) { ls[i].previous = NULL; } for (i = 0; ngx_modules[i]; i++) { if (ngx_modules[i]->init_process) { if (ngx_modules[i]->init_process(cycle) == NGX_ERROR) { /* fatal */ exit(2); } } } for (n = 0; n < ngx_last_process; n++) { if (ngx_processes[n].pid == -1) { continue; } if (n == ngx_process_slot) { continue; } if (ngx_processes[n].channel[1] == -1) { continue; } if (close(ngx_processes[n].channel[1]) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "close() channel failed"); } } if (close(ngx_processes[ngx_process_slot].channel[0]) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "close() channel failed"); } #if 0 ngx_last_process = 0; #endif if (ngx_add_channel_event(cycle, ngx_channel, NGX_READ_EVENT, ngx_channel_handler) == NGX_ERROR) { /* fatal */ exit(2); } }
static int daemonize(int background, char *pidfile) { FILE *fp; pid_t childpid; pid_t pid_ret; int fd; int ret; char *runas; if (background) { fp = fopen(pidfile, "w"); if (!fp) { DPRINTF(E_LOG, L_MAIN, "Error opening pidfile (%s): %s\n", pidfile, strerror(errno)); return -1; } fd = open("/dev/null", O_RDWR, 0); if (fd < 0) { DPRINTF(E_LOG, L_MAIN, "Error opening /dev/null: %s\n", strerror(errno)); fclose(fp); return -1; } signal(SIGTTOU, SIG_IGN); signal(SIGTTIN, SIG_IGN); signal(SIGTSTP, SIG_IGN); childpid = fork(); if (childpid > 0) exit(EXIT_SUCCESS); else if (childpid < 0) { DPRINTF(E_FATAL, L_MAIN, "Fork failed: %s\n", strerror(errno)); close(fd); fclose(fp); return -1; } pid_ret = setsid(); if (pid_ret == (pid_t) -1) { DPRINTF(E_FATAL, L_MAIN, "setsid() failed: %s\n", strerror(errno)); close(fd); fclose(fp); return -1; } logger_detach(); dup2(fd, STDIN_FILENO); dup2(fd, STDOUT_FILENO); dup2(fd, STDERR_FILENO); if (fd > 2) close(fd); ret = chdir("/"); if (ret < 0) DPRINTF(E_WARN, L_MAIN, "chdir() failed: %s\n", strerror(errno)); umask(0); fprintf(fp, "%d\n", getpid()); fclose(fp); DPRINTF(E_DBG, L_MAIN, "PID: %d\n", getpid()); } if (geteuid() == (uid_t) 0) { runas = cfg_getstr(cfg_getsec(cfg, "general"), "uid"); ret = initgroups(runas, runas_gid); if (ret != 0) { DPRINTF(E_FATAL, L_MAIN, "initgroups() failed: %s\n", strerror(errno)); return -1; } ret = setegid(runas_gid); if (ret != 0) { DPRINTF(E_FATAL, L_MAIN, "setegid() failed: %s\n", strerror(errno)); return -1; } ret = seteuid(runas_uid); if (ret != 0) { DPRINTF(E_FATAL, L_MAIN, "seteuid() failed: %s\n", strerror(errno)); return -1; } } return 0; }
int main(int argc, char **argv) { apeconfig *srv; int random, im_r00t = 0, pidfd = 0, serverfd; unsigned int getrandom = 0, ct_id; const char *pidfile = NULL; char *confs_path = NULL; struct _fdevent fdev; char cfgfile[513] = APE_CONFIG_FILE; acetables *g_ape; if (argc > 1 && strcmp(argv[1], "--version") == 0) { printf("\n AJAX Push Engine Server %s - (C) Anthony Catel <*****@*****.**>\n http://www.ape-project.org/\n\n", _VERSION); return 0; } if (argc > 1 && strcmp(argv[1], "--help") == 0) { printf("\n AJAX Push Engine Server %s - (C) Anthony Catel <*****@*****.**>\n http://www.ape-project.org/\n", _VERSION); printf("\n usage: aped [options]\n\n"); printf(" Options:\n --help : Display this help\n --version : Show version number\n --cfg <config path>: Load a specific config file (default is %s)\n\n", cfgfile); return 0; } else if (argc > 2 && strcmp(argv[1], "--cfg") == 0) { memset(cfgfile, 0, 513); strncpy(cfgfile, argv[2], 512); confs_path = get_path(cfgfile); } else if (argc > 1) { printf("\n AJAX Push Engine Server %s - (C) Anthony Catel <*****@*****.**>\n http://www.ape-project.org/\n\n", _VERSION); printf(" Unknown parameters - check \"aped --help\"\n\n"); return 0; } if (NULL == (srv = ape_config_load(cfgfile))) { printf("\nExited...\n\n"); exit(1); } if (getuid() == 0) { im_r00t = 1; } signal(SIGINT, &signal_handler); signal(SIGTERM, &signal_handler); if (VTICKS_RATE < 1) { printf("[ERR] TICKS_RATE cant be less than 1\n"); return 0; } random = open("/dev/urandom", O_RDONLY); if (!random) { printf("Cannot open /dev/urandom... exiting\n"); return 0; } read(random, &getrandom, 3); srand(getrandom); close(random); g_ape = xmalloc(sizeof(*g_ape)); g_ape->basemem = 1; // set 1 for testing if growup works g_ape->srv = srv; g_ape->confs_path = confs_path; g_ape->is_daemon = 0; ape_log_init(g_ape); fdev.handler = EVENT_UNKNOWN; #ifdef USE_EPOLL_HANDLER fdev.handler = EVENT_EPOLL; #endif #ifdef USE_KQUEUE_HANDLER fdev.handler = EVENT_KQUEUE; #endif g_ape->co = xmalloc(sizeof(*g_ape->co) * g_ape->basemem); memset(g_ape->co, 0, sizeof(*g_ape->co) * g_ape->basemem); g_ape->bad_cmd_callbacks = NULL; g_ape->bufout = xmalloc(sizeof(struct _socks_bufout) * g_ape->basemem); g_ape->timers.timers = NULL; g_ape->timers.ntimers = 0; g_ape->events = &fdev; if (events_init(g_ape, &g_ape->basemem) == -1) { printf("Fatal error: APE compiled without an event handler... exiting\n"); return 0; }; serverfd = servers_init(g_ape); ape_log(APE_INFO, __FILE__, __LINE__, g_ape, "APE starting up - pid : %i", getpid()); if (strcmp(CONFIG_VAL(Server, daemon, srv), "yes") == 0 && (pidfile = CONFIG_VAL(Server, pid_file, srv)) != NULL) { if ((pidfd = open(pidfile, O_TRUNC | O_WRONLY | O_CREAT, 0655)) == -1) { ape_log(APE_WARN, __FILE__, __LINE__, g_ape, "Cant open pid file : %s", CONFIG_VAL(Server, pid_file, srv)); } } if (im_r00t) { struct group *grp = NULL; struct passwd *pwd = NULL; if (inc_rlimit(atoi(CONFIG_VAL(Server, rlimit_nofile, srv))) == -1) { ape_log(APE_WARN, __FILE__, __LINE__, g_ape, "Cannot set the max filedescriptos limit (setrlimit) %s", strerror(errno)); } /* Set uid when uid section exists */ if (ape_config_get_section(srv, "uid")) { /* Get the user information (uid section) */ if ((pwd = getpwnam(CONFIG_VAL(uid, user, srv))) == NULL) { ape_log(APE_ERR, __FILE__, __LINE__, g_ape, "Can\'t find username %s", CONFIG_VAL(uid, user, srv)); return -1; } if (pwd->pw_uid == 0) { ape_log(APE_ERR, __FILE__, __LINE__, g_ape, "%s uid can\'t be 0", CONFIG_VAL(uid, user, srv)); return -1; } /* Get the group information (uid section) */ if ((grp = getgrnam(CONFIG_VAL(uid, group, srv))) == NULL) { printf("[ERR] Can\'t find group %s\n", CONFIG_VAL(uid, group, srv)); ape_log(APE_ERR, __FILE__, __LINE__, g_ape, "Can\'t find group %s", CONFIG_VAL(uid, group, srv)); return -1; } if (grp->gr_gid == 0) { ape_log(APE_ERR, __FILE__, __LINE__, g_ape, "%s gid can\'t be 0", CONFIG_VAL(uid, group, srv)); return -1; } setgid(grp->gr_gid); setgroups(0, NULL); initgroups(CONFIG_VAL(uid, user, srv), grp->gr_gid); setuid(pwd->pw_uid); } } else { printf("[WARN] You have to run \'aped\' as root to increase r_limit\n"); ape_log(APE_WARN, __FILE__, __LINE__, g_ape, "You have to run \'aped\' as root to increase r_limit"); } if (strcmp(CONFIG_VAL(Server, daemon, srv), "yes") == 0) { ape_log(APE_INFO, __FILE__, __LINE__, g_ape, "Starting daemon"); ape_daemon(pidfd, g_ape); events_reload(g_ape->events); events_add(g_ape->events, serverfd, EVENT_READ); } if (!g_ape->is_daemon) { printf(" _ ___ ___ \n"); printf(" /_\\ | _ \\ __|\n"); printf(" / _ \\| _/ _| \n"); printf("/_/ \\_\\_| |___|\nAJAX Push Engine\n\n"); printf("Bind on port %i\n\n", atoi(CONFIG_VAL(Server, port, srv))); printf("Version : %s\n", _VERSION); printf("Build : %s %s\n", __DATE__, __TIME__); printf("Author : Weelya ([email protected])\n\n"); } signal(SIGPIPE, SIG_IGN); ape_dns_init(g_ape); g_ape->cmd_hook.head = NULL; g_ape->cmd_hook.foot = NULL; g_ape->hSessid = hashtbl_init(); g_ape->hChannel = hashtbl_init(); g_ape->hPubid = hashtbl_init(); g_ape->proxy.list = NULL; g_ape->proxy.hosts = NULL; g_ape->hCallback = hashtbl_init(); g_ape->uHead = NULL; g_ape->cHead = NULL; g_ape->nConnected = 0; g_ape->plugins = NULL; g_ape->properties = NULL; ct_id = add_ticked(check_timeout, g_ape)->identifier; do_register(g_ape); transport_start(g_ape); findandloadplugin(g_ape); server_is_running = 1; server_is_shutdowning = 0; /* Starting Up */ sockroutine(g_ape); /* loop */ /* Shutdown */ if (pidfile != NULL) { unlink(pidfile); } free(confs_path); ape_dns_free(g_ape); del_timer_identifier(ct_id, g_ape); events_free(g_ape); transport_free(g_ape); hashtbl_free(g_ape->hSessid, 0); hashtbl_free(g_ape->hChannel, 0); hashtbl_free(g_ape->hPubid, 0); do_unregister(g_ape); hashtbl_free(g_ape->hCallback, 1); ape_config_free(srv); int i; for (i = 0; i < g_ape->basemem; i++) { if (g_ape->co[i] != NULL) { close_socket(i, g_ape); free(g_ape->co[i]); } } free(g_ape->co); free(g_ape->bufout); free_all_hook_cmd(g_ape); free_all_plugins(g_ape); free(g_ape); return 0; }