[PATCH] eventfd: new command

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This is a new utility that makes it possible and easy to make use of
eventfd(2) IPC mechanism in shell scripts. It can be used for signalling
between userspace processes and userspace and kernel (currently, cgroup
notifications use this).

Signed-off-by: Alexander Shishkin <virtuoso@xxxxxxxxx>
---
 configure.ac          |    2 +
 sys-utils/.gitignore  |    1 +
 sys-utils/Makefile.am |    2 +-
 sys-utils/eventfd.1   |   51 ++++++++++
 sys-utils/eventfd.c   |  249 +++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 304 insertions(+), 1 deletions(-)
 create mode 100644 sys-utils/eventfd.1
 create mode 100644 sys-utils/eventfd.c

diff --git a/configure.ac b/configure.ac
index eaa5945..533c05c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -673,6 +673,8 @@ dnl unshare could be available as libc function or as syscall only
 UTIL_CHECK_SYSCALL([unshare])
 AC_CHECK_FUNCS([unshare])
 
+AC_CHECK_FUNCS([eventfd])
+
 AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
 #include <time.h>
 #include <unistd.h>
diff --git a/sys-utils/.gitignore b/sys-utils/.gitignore
index 7af5ffb..44d1f0d 100644
--- a/sys-utils/.gitignore
+++ b/sys-utils/.gitignore
@@ -2,6 +2,7 @@ arch
 ctrlaltdel
 cytune
 dmesg
+eventfd
 fallocate
 flock
 fsfreeze
diff --git a/sys-utils/Makefile.am b/sys-utils/Makefile.am
index 166d718..f68edd5 100644
--- a/sys-utils/Makefile.am
+++ b/sys-utils/Makefile.am
@@ -11,7 +11,7 @@ dist_man_MANS = flock.1 ipcrm.1 ipcs.1 ipcmk.1 renice.1 setsid.1 \
 if LINUX
 bin_PROGRAMS += dmesg
 sbin_PROGRAMS += ctrlaltdel fsfreeze
-usrbin_exec_PROGRAMS += cytune setarch
+usrbin_exec_PROGRAMS += cytune setarch eventfd
 usrsbin_exec_PROGRAMS += ldattach tunelp rtcwake
 
 dist_man_MANS += dmesg.1 ctrlaltdel.8 cytune.8 setarch.8 \
diff --git a/sys-utils/eventfd.1 b/sys-utils/eventfd.1
new file mode 100644
index 0000000..ee557d2
--- /dev/null
+++ b/sys-utils/eventfd.1
@@ -0,0 +1,51 @@
+.TH EVENTFD 1 "August 2010" Linux "User Manuals"
+.SH NAME
+eventfd - create, write and poll eventfds.
+.SH SYNOPSIS
+eventfd
+.RI [ OPTIONS ]
+.RI [ PROGRAM ]
+.RI [ ARGUMENTS ]
+.SH DESCRIPTION
+.PP
+Makes it possible to use eventfds in shell scripts. It has two general modes of operation: opening an eventfd and writing to eventfd.
+.TP
+.BR "Opening an eventfd."
+Unless -e argument is used, an eventfd descriptor will be created with the initial value 0 (unless otherwise specified by -i argument). The number of this descriptor is stored in an environment variable \fBEVENTFD\fP (or any other name, specified with -n argument), then a new process is started, which is either a \fBPROGRAM\fP specified on the command line or a shell. If -w argument has been specified, another process will be started, which will poll the newly created eventfd and run a given command every time it encounters new signals. The value read from eventfd will be placed in \fB__EVENTFDVAL\fP environment variable then.
+.TP
+.BR "Writing to an eventfd."
+If -e argument is used, \fBeventfd\fP will write the given number to a eventfd descriptor as found in \fBEVENTFD\fP variable (or any other variable specified with -n argument).
+.SH OPTIONS
+.TP
+\fB\-F\fR, \fB\-\-nofork\fR
+exec the main process without forking; it will be the process' responsibility to reap the watcher process if it has been created
+.TP
+\fB\-e\fR, \fB\-\-echo\fR
+echo a number into eventfd
+.TP
+\fB\-n\fR, \fB\-\-varname\fR
+eventfd variable name (defaults to \fBEVENTFD\fP)
+.TP
+\fB\-w\fR, \fB\-\-watchcmd\fR
+command to launch upon signal arrival (by default, no command will be launched and no polling performed)
+.TP
+\fB\-i\fR, \fB\-\-init\fR
+initial value of the eventfd counter (defaults to 0)
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+print help message
+.SH EXAMPLE
+\&
+.nf
+$ eventfd -n PINGFD -w 'printf "ping received (%d)" $__EVENTFDVAL'
+$ eventfd -n PINGFD -e 1
+ping received (1)
+$
+.SH "SEE ALSO"
+eventfd(2).
+
+.SH BUGS
+None found so far.
+
+.SH AUTHOR
+Alexander Shishkin <virtuoso@xxxxxxxxx>
diff --git a/sys-utils/eventfd.c b/sys-utils/eventfd.c
new file mode 100644
index 0000000..42f1522
--- /dev/null
+++ b/sys-utils/eventfd.c
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2010 Nokia Corporation
+ * Written by Alexander Shishkin <virtuoso@xxxxxxxxx>
+ *
+ * This file is a part of util-linux-ng.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <getopt.h>
+#include <signal.h>
+#include <poll.h>
+
+#include "nls.h"
+
+#ifndef HAVE_EVENTFD
+# include <sys/syscall.h>
+
+static int eventfd(unsigned int initval, int flags)
+{
+	return syscall(SYS_eventfd2, initval, flags);
+}
+#else
+# include <sys/eventfd.h>
+#endif
+
+#define strtoeventfd(x)					\
+	({ sizeof(long) == 4				\
+			? strtoull(optarg, NULL, 10)	\
+			: strtoul(optarg, NULL, 10); })
+
+static void watcher(int fd, const char *cmd)
+{
+	struct pollfd fds = { .fd = fd, .events = POLLIN };
+
+	while (poll(&fds, 1, -1) > 0) {
+		uint64_t efdval;
+		size_t len;
+		char s[BUFSIZ];
+
+		len = read(fd, &efdval, sizeof efdval);
+		if (len != sizeof(uint64_t)) {
+			perror(_("short read from eventfd"));
+			exit(EXIT_FAILURE);
+		}
+
+		snprintf(s, BUFSIZ, "%lu", efdval);
+
+		setenv("__EVENTFDVAL", s, 1);
+		system(cmd);
+	}
+
+	fprintf(stderr, _("eventfd poll failed\n"));
+}
+
+static pid_t watcher_pid = -1;
+
+static void watcher_start(int fd, char *watchcmd)
+{
+	watcher_pid = fork();
+
+	if (watcher_pid < 0) {
+		perror(_("fork failed"));
+		exit(EXIT_FAILURE);
+	} else if (!watcher_pid)
+		watcher(fd, watchcmd);
+}
+
+/* Quis custodiet ipsos custodes? */
+static void watcher_reap(void)
+{
+	if (watcher_pid <= 0)
+		return;
+
+	kill(watcher_pid, SIGTERM);
+	while (waitpid(watcher_pid, NULL, 0) != watcher_pid)
+		;
+}
+
+static int writer(char *varname, uint64_t echoval)
+{
+	char *fdstr = getenv(varname);
+	int fd;
+	ssize_t len;
+
+	if (!fdstr) {
+		fprintf(stderr, _("$%s is empty\n"), varname);
+		return EXIT_FAILURE;
+	}
+
+	fd = atoi(fdstr);
+	len = write(fd, &echoval, 8);
+	if (len == -1) {
+		perror(_("write failed"));
+		return EXIT_FAILURE;
+	}
+
+	return EXIT_SUCCESS;
+}
+
+static const struct option options[] = {
+	{ "nofork",	0, 0, 'F' },
+	{ "echo",	1, 0, 'e' },
+	{ "varname",	1, 0, 'n' },
+	{ "watchcmd",	1, 0, 'w' },
+	{ "init",	1, 0, 'i' },
+	{ "help",	0, 0, 'h' },
+	{ NULL,		0, 0, 0   },
+};
+
+static const char *optdescs[] = {
+	"exec the main process without forking",
+	"echo a number into eventfd",
+	"eventfd variable name",
+	"command to launch upon signal arrival",
+	"initial value of the eventfd counter",
+	"print help message"
+};
+
+static void usage(int exitcode)
+{
+	int i;
+
+	printf(_("usage: %s [OPTIONS] <program> ...\n\n"
+		 "Create, write and poll eventfds.\nOPTIONS:\n"),
+		program_invocation_short_name);
+	for (i = 0; options[i].name; i++)
+		printf("  -%c, --%-10s %s\n", options[i].val, options[i].name,
+		       _(optdescs[i]));
+
+	exit(exitcode);
+}
+
+static const char optstr[] = "Fe:n:w:i:h";
+
+int main(int argc, char *argv[])
+{
+	uint64_t echoval = 0;
+	unsigned int initval = 0;
+	int fd, c, loptidx, nofork = 0;
+	char *varname = "EVENTFD";
+	char val[BUFSIZ];
+	char **nargv;
+	char *watchcmd = NULL;
+
+	for (;;) {
+		c = getopt_long(argc, argv, optstr, options, NULL);
+		if (c == -1)
+			break;
+
+		switch (c) {
+			case 'e':
+				echoval = strtoeventfd(optarg);
+				break;
+			case 'i':
+				initval = strtoul(optarg, NULL, 10);
+				break;
+			case 'n':
+				varname = strdup(optarg);
+				break;
+			case 'F':
+				nofork++;
+				break;
+			case 'w':
+				watchcmd = strdup(optarg);
+				break;
+			case 'h':
+				usage(EXIT_SUCCESS);
+			default:
+				usage(EXIT_FAILURE);
+		}
+	}
+
+	if (echoval)
+		return writer(varname, echoval);
+
+	fd = eventfd(initval, 0);
+	if (fd == -1) {
+		perror(_("eventfd failed"));
+		return EXIT_FAILURE;
+	}
+
+	/*
+	 * if there are more arguments, make an argv of them,
+	 * otherwise, make it be the shell
+	 */
+	if (optind < argc) {
+		nargv = malloc((argc - optind + 1) * sizeof(char *));
+		if (!nargv) {
+			fputs(_("Out of memory\n"), stderr);
+			return EXIT_FAILURE;
+		}
+
+		for (c = 0; optind < argc; optind++, c++)
+			nargv[c] = strdup(argv[optind]);
+
+		nargv[c] = NULL;
+	} else {
+		nargv = malloc(2 * sizeof(char *));
+		if (!nargv) {
+			fputs(_("Out of memory\n"), stderr);
+			return EXIT_FAILURE;
+		}
+
+		nargv[0] = getenv("SHELL");
+		if (!nargv[0])
+			nargv[0] = "/bin/sh";
+
+		nargv[1] = NULL;
+	}
+
+	if (watchcmd)
+		watcher_start(fd, watchcmd);
+
+	/* set $varname to eventfd descriptor */
+	snprintf(val, BUFSIZ, "%d", fd);
+	setenv(varname, val, 1);
+
+	if (!nofork) {
+		pid_t mpid = fork();
+		if (mpid < 0) {
+			perror(_("fork failed"));
+			watcher_reap();
+			return EXIT_FAILURE;
+		} else if (!mpid)
+			return execv(nargv[0], nargv);
+		else {
+			waitpid(mpid, NULL, 0);
+			watcher_reap();
+			return EXIT_SUCCESS;
+		}
+	}
+
+	return execv(nargv[0], nargv);
+}
-- 
1.7.2.rc1.1.gb2842

--
To unsubscribe from this list: send the line "unsubscribe util-linux-ng" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Netdev]     [Ethernet Bridging]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux