/* set up the socket for client connections */ void open_sock() { int fd; struct connection c; /* if this is a socket passed in via stdin by systemd */ if (is_socket(STDIN_FILENO)) { fd = STDIN_FILENO; /* ??? Move CLOEXEC and NONBLOCK settings below up to here? */ } else { /* create our own socket */ fd = ud_create_socket(socketfile, socketmode); if (fd < 0) { acpid_log(LOG_ERR, "can't open socket %s: %s", socketfile, strerror(errno)); exit(EXIT_FAILURE); } /* if we need to change the socket's group, do so */ if (socketgroup) { struct group *gr; struct stat buf; gr = getgrnam(socketgroup); if (!gr) { acpid_log(LOG_ERR, "group %s does not exist", socketgroup); exit(EXIT_FAILURE); } if (fstat(fd, &buf) < 0) { acpid_log(LOG_ERR, "can't stat %s: %s", socketfile, strerror(errno)); exit(EXIT_FAILURE); } /* ??? I've tried using fchown(), however it doesn't work here. * It also doesn't work before bind(). The GNU docs seem to * indicate it isn't supposed to work with sockets. */ /* if (fchown(fd, buf.st_uid, gr->gr_gid) < 0) { */ if (chown(socketfile, buf.st_uid, gr->gr_gid) < 0) { acpid_log(LOG_ERR, "can't chown %s: %s", socketfile, strerror(errno)); exit(EXIT_FAILURE); } } } /* Don't leak fds when execing. * ud_create_socket() already does this, but there is no guarantee that * a socket sent in via STDIN will have this set. */ if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) { close(fd); acpid_log(LOG_ERR, "fcntl() on socket %s for FD_CLOEXEC: %s", socketfile, strerror(errno)); return; } /* Avoid a potential hang. * ud_create_socket() already does this, but there is no guarantee that * a socket sent in via STDIN will have this set. */ if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) { close(fd); acpid_log(LOG_ERR, "fcntl() on socket %s for O_NONBLOCK: %s", socketfile, strerror(errno)); return; } /* add a connection to the list */ c.fd = fd; c.process = process_sock; c.pathname = NULL; c.kybd = 0; if (add_connection(&c) < 0) { close(fd); acpid_log(LOG_ERR, "can't add connection for socket %s", socketfile); return; } }
int main(int argc, char **argv) { int event_fd; int sock_fd = -1; /* init to avoid a compiler warning */ /* learn who we really are */ progname = (const char *)strrchr(argv[0], '/'); progname = progname ? (progname + 1) : argv[0]; /* handle the commandline */ handle_cmdline(&argc, &argv); /* close any extra file descriptors */ close_fds(); /* actually open the event file */ event_fd = open(eventfile, O_RDONLY); if (event_fd < 0) { fprintf(stderr, "%s: can't open %s: %s\n", progname, eventfile, strerror(errno)); exit(EXIT_FAILURE); } fcntl(event_fd, F_SETFD, FD_CLOEXEC); /* * if there is data, and the kernel is NOT broken, this eats 1 byte. We * can't have that. This is ifdef'ed out on the assumption that old kernels * are out of popular use, by now. */ #ifdef TEST_FOR_BAD_KERNELS /* * Older kernels did not support read() properly or poll() at all * Check that the kernel supports the proper semantics, or die. * * Good kernels will respect O_NONBLOCK and return -1. Bad kernels * will ignore O_NONBLOCK and return 0. Really bad kernels will block * and overflow the buffer. Can't deal with the really bad ones. */ { int fl; char buf; fl = fcntl(event_fd, F_GETFL); fcntl(event_fd, F_SETFL, fl | O_NONBLOCK); if (read(event_fd, &buf, 1) == 0) { fprintf(stderr, "%s: this kernel does not support proper " "event file handling.\n" "Please get the patch from " "http://acpid.sourceforge.net\n", progname); exit(EXIT_FAILURE); } fcntl(event_fd, F_SETFL, fl); } #endif /* open our socket */ if (!nosocket) { sock_fd = ud_create_socket(socketfile); if (sock_fd < 0) { fprintf(stderr, "%s: can't open socket %s: %s\n", progname, socketfile, strerror(errno)); exit(EXIT_FAILURE); } fcntl(sock_fd, F_SETFD, FD_CLOEXEC); chmod(socketfile, socketmode); if (socketgroup) { struct group *gr; struct stat buf; gr = getgrnam(socketgroup); if (!gr) { fprintf(stderr, "%s: group %s does not exist\n", progname, socketgroup); exit(EXIT_FAILURE); } if (stat(socketfile, &buf) < 0) { fprintf(stderr, "%s: can't stat %s\n", progname, socketfile); exit(EXIT_FAILURE); } if (chown(socketfile, buf.st_uid, gr->gr_gid) < 0) { fprintf(stderr, "%s: chown(): %s\n", progname, strerror(errno)); exit(EXIT_FAILURE); } } } /* if we're running in foreground, we don't daemonize */ if (!foreground) { if (daemonize() < 0) exit(EXIT_FAILURE); } /* open the log */ if (open_log() < 0) { exit(EXIT_FAILURE); } acpid_log(LOG_INFO, "starting up\n"); /* trap key signals */ signal(SIGHUP, reload_conf); signal(SIGINT, clean_exit); signal(SIGQUIT, clean_exit); signal(SIGTERM, clean_exit); signal(SIGPIPE, SIG_IGN); /* read in our configuration */ if (acpid_read_conf(confdir)) { exit(EXIT_FAILURE); } /* create our pidfile */ if (create_pidfile() < 0) { exit(EXIT_FAILURE); } /* main loop */ acpid_log(LOG_INFO, "waiting for events: event logging is %s\n", logevents ? "on" : "off"); while (1) { struct pollfd ar[2]; int r; int fds = 0; /* poll for the socket and the event file */ ar[0].fd = event_fd; ar[0].events = POLLIN; fds++; if (!nosocket) { ar[1].fd = sock_fd; ar[1].events = POLLIN; fds++; } r = poll(ar, fds, -1); if (r < 0 && errno == EINTR) { continue; } else if (r < 0) { acpid_log(LOG_ERR, "poll(): %s\n", strerror(errno)); continue; } /* house keeping */ acpid_close_dead_clients(); /* was it an event? */ if (ar[0].revents) { char *event; struct stat trash; int fexists; /* check for existence of a lockfile */ fexists = (stat(lockfile, &trash) == 0); /* this shouldn't happen */ if (!ar[0].revents & POLLIN) { acpid_log(LOG_DEBUG, "odd, poll set flags 0x%x\n", ar[0].revents); continue; } /* read an event */ event = read_line(event_fd); /* if we're locked, don't process the event */ if (fexists) { if (logevents) { acpid_log(LOG_INFO, "lockfile present, not processing " "event \"%s\"\n", event); } continue; } /* handle the event */ if (event) { if (logevents) { acpid_log(LOG_INFO, "received event \"%s\"\n", event); } acpid_handle_event(event); if (logevents) { acpid_log(LOG_INFO, "completed event \"%s\"\n", event); } } else if (errno == EPIPE) { acpid_log(LOG_WARNING, "events file connection closed\n"); break; } else { static int nerrs; if (++nerrs >= ACPID_MAX_ERRS) { acpid_log(LOG_ERR, "too many errors reading " "events file - aborting\n"); break; } } } /* was it a new connection? */ if (!nosocket && ar[1].revents) { int cli_fd; struct ucred creds; char buf[32]; static int accept_errors; /* this shouldn't happen */ if (!ar[1].revents & POLLIN) { acpid_log(LOG_DEBUG, "odd, poll set flags 0x%x\n", ar[1].revents); continue; } /* accept and add to our lists */ cli_fd = ud_accept(sock_fd, &creds); if (cli_fd < 0) { acpid_log(LOG_ERR, "can't accept client: %s\n", strerror(errno)); accept_errors++; if (accept_errors >= 5) { acpid_log(LOG_ERR, "giving up\n"); clean_exit_with_status(EXIT_FAILURE); } continue; } accept_errors = 0; if (creds.uid != 0 && non_root_clients >= clientmax) { close(cli_fd); acpid_log(LOG_ERR, "too many non-root clients\n"); continue; } if (creds.uid != 0) { non_root_clients++; } fcntl(cli_fd, F_SETFD, FD_CLOEXEC); snprintf(buf, sizeof(buf)-1, "%d[%d:%d]", creds.pid, creds.uid, creds.gid); acpid_add_client(cli_fd, buf); } } clean_exit_with_status(EXIT_SUCCESS); return 0; }