Пример #1
0
/* 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;
	}
}
Пример #2
0
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;
}