Re: [PATCH v2] wall: add --group option

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

 



On Wed, 16 Nov 2016, Jim Patterson wrote:

> The wall command on AIX supports a "-g" option to limit the message
> to a group of users by gid.  Add compatibility to the Linux version.

A bit more work to this change: 1) numeric gid is no checked to be valid, 
2) gid is checked to be withing range of gid_t and 3) --group option is 
added to bash completion hints 4) add Reviewed-by line.

This change is also available from here:
git://github.com/kerolasa/lelux-utiliteetit.git wall-group

--->8----
From: Jim Patterson <jimp@xxxxxxxxxx>
Date: Wed, 16 Nov 2016 12:22:14 -0500
Subject: [PATCH] wall: add --group option

The wall command on AIX supports a "-g" option to limit the message
to a group of users by gid.  Add compatibility to the Linux version.

Thanks to Sami Kerola <kerolasa@xxxxxx> for an initial skeleton
implementation.

Reference: http://www.ibm.com/support/knowledgecenter/ssw_aix_61/com.ibm.aix.cmds6/wall.htm
Reviewed-by: Sami Kerola <kerolasa@xxxxxx>
Signed-off-by: Jim Patterson <jimp@xxxxxxxxxx>
---
 bash-completion/wall |  6 ++++-
 term-utils/wall.1    |  7 +++++
 term-utils/wall.c    | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 87 insertions(+), 2 deletions(-)

diff --git a/bash-completion/wall b/bash-completion/wall
index e3145ff..d2fba9a 100644
--- a/bash-completion/wall
+++ b/bash-completion/wall
@@ -9,13 +9,17 @@ _wall_module()
 			COMPREPLY=( $(compgen -W "seconds" -- $cur) )
 			return 0
 			;;
+		'-g'|'--group')
+			COMPREPLY=( $(compgen -A 'group' -- $cur) )
+			return 0
+			;;
 		'-h'|'--help'|'-V'|'--version')
 			return 0
 			;;
 	esac
 	case $cur in
 		-*)
-			OPTS="--nobanner --timeout --version --help"
+			OPTS="--group --nobanner --timeout --version --help"
 			COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) )
 			return 0
 			;;
diff --git a/term-utils/wall.1 b/term-utils/wall.1
index 33cd352..a7da195 100644
--- a/term-utils/wall.1
+++ b/term-utils/wall.1
@@ -39,6 +39,8 @@ wall \- write a message to all users
 .RB [ \-n ]
 .RB [ \-t
 .IR timeout ]
+.RB [ \-g
+.IR group ]
 .RI [ message " | " file ]
 .SH DESCRIPTION
 .B wall
@@ -68,6 +70,11 @@ This \fItimeout\fR must be a positive integer.  The default value
 is 300 seconds, which is a legacy from the time when people ran terminals over
 modem lines.
 .TP
+.BR \-g , " \-\-group " \fIgroup\fR
+Limit printing message to members of group defined as a
+.I group
+argument.  The argument can be group name or gid.
+.TP
 .BR \-V , " \-\-version"
 Display version information and exit.
 .TP
diff --git a/term-utils/wall.c b/term-utils/wall.c
index 4ad94d8..9c89f3e 100644
--- a/term-utils/wall.c
+++ b/term-utils/wall.c
@@ -58,6 +58,9 @@
 #include <unistd.h>
 #include <utmp.h>
 #include <getopt.h>
+#include <sys/types.h>
+#include <grp.h>
+#include <linux/sysctl.h>
 
 #include "nls.h"
 #include "xalloc.h"
@@ -71,6 +74,7 @@
 
 #define	TERM_WIDTH	79
 #define	WRITE_TIME_OUT	300		/* in seconds */
+#define GID_T_MAX (~(gid_t)0)
 
 /* Function prototypes */
 static char *makemsg(char *fname, char **mvec, int mvecsz,
@@ -86,6 +90,7 @@ static void __attribute__((__noreturn__)) usage(FILE *out)
 	fputs(_("Write a message to all users.\n"), out);
 
 	fputs(USAGE_OPTIONS, out);
+	fputs(_(" -g, --group <group>     only send message to group\n"), out);
 	fputs(_(" -n, --nobanner          do not print banner, works only for root\n"), out);
 	fputs(_(" -t, --timeout <timeout> write timeout in seconds\n"), out);
 	fputs(USAGE_SEPARATOR, out);
@@ -96,6 +101,63 @@ static void __attribute__((__noreturn__)) usage(FILE *out)
 	exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
 }
 
+struct group_workspace {
+	gid_t	requested_group;
+	int	max;
+	gid_t	*groups;
+};
+
+static gid_t get_group_gid(const char *optarg)
+{
+	struct group *gr;
+	uint64_t gid;
+
+	if ((gr = getgrnam(optarg)))
+		return gr->gr_gid;
+	gid = strtou64_or_err(optarg, _("invalid group argument"));
+	if (GID_T_MAX < gid)
+		errx(EXIT_FAILURE, "%s: %s", _("gid out of range"), optarg);
+	if (!getgrgid(gid))
+		errx(EXIT_FAILURE, "%s: %s", _("unknown gid"), optarg);
+	return gid;
+}
+
+static struct group_workspace *init_group_workspace(const char *optarg)
+{
+	struct group_workspace *buf = xmalloc(sizeof(struct group_workspace));
+
+	buf->requested_group = get_group_gid(optarg);
+	buf->max = sysconf(_SC_NGROUPS_MAX) + 1;	/* room for the primary gid */
+	buf->groups = xcalloc(sizeof(gid_t), buf->max);
+
+	return buf;
+}
+
+static int is_gr_member(const char *login, const struct group_workspace *buf)
+{
+	struct passwd *pw;
+	int max_groups = buf->max;
+	int rc;
+
+	pw = getpwnam(login);
+	if (buf->requested_group == pw->pw_gid)
+		return TRUE;
+
+	rc = getgrouplist(login, pw->pw_gid, buf->groups, &max_groups);
+	if (rc < 0) {
+		/* buffer too small, not sure how this can happen, since
+		   we used sysconf to get the size... */
+		errx(EXIT_FAILURE,
+			_("getgrouplist found more groups than sysconf allows"));
+	}
+	while (max_groups >= 0) {
+		if (buf->requested_group == buf->groups[max_groups])
+			return TRUE;
+		max_groups--;
+	}
+	return FALSE;
+}
+
 int main(int argc, char **argv)
 {
 	int ch;
@@ -104,6 +166,7 @@ int main(int argc, char **argv)
 	char *p;
 	char line[sizeof(utmpptr->ut_line) + 1];
 	int print_banner = TRUE;
+	struct group_workspace *group_buf = NULL;
 	char *mbuf, *fname = NULL;
 	size_t mbufsize;
 	unsigned timeout = WRITE_TIME_OUT;
@@ -113,6 +176,7 @@ int main(int argc, char **argv)
 	static const struct option longopts[] = {
 		{ "nobanner",	no_argument,		0, 'n' },
 		{ "timeout",	required_argument,	0, 't' },
+		{ "group",	required_argument,	0, 'g' },
 		{ "version",	no_argument,		0, 'V' },
 		{ "help",	no_argument,		0, 'h' },
 		{ NULL,	0, 0, 0 }
@@ -123,7 +187,7 @@ int main(int argc, char **argv)
 	textdomain(PACKAGE);
 	atexit(close_stdout);
 
-	while ((ch = getopt_long(argc, argv, "nt:Vh", longopts, NULL)) != -1) {
+	while ((ch = getopt_long(argc, argv, "nt:g:Vh", longopts, NULL)) != -1) {
 		switch (ch) {
 		case 'n':
 			if (geteuid() == 0)
@@ -136,6 +200,9 @@ int main(int argc, char **argv)
 			if (timeout < 1)
 				errx(EXIT_FAILURE, _("invalid timeout argument: %s"), optarg);
 			break;
+		case 'g':
+			group_buf = init_group_workspace(optarg);
+			break;
 		case 'V':
 			printf(UTIL_LINUX_VERSION);
 			exit(EXIT_SUCCESS);
@@ -172,12 +239,19 @@ int main(int argc, char **argv)
 		if (utmpptr->ut_line[0] == ':')
 			continue;
 
+		if (group_buf && !is_gr_member(utmpptr->ut_user, group_buf))
+			continue;
+
 		xstrncpy(line, utmpptr->ut_line, sizeof(utmpptr->ut_line));
 		if ((p = ttymsg(&iov, 1, line, timeout)) != NULL)
 			warnx("%s", p);
 	}
 	endutent();
 	free(mbuf);
+	if (group_buf) {
+		free(group_buf->groups);
+		free(group_buf);
+	}
 	exit(EXIT_SUCCESS);
 }
 
-- 
2.10.2

--
To unsubscribe from this list: send the line "unsubscribe util-linux" 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