summaryrefslogtreecommitdiffstats
path: root/term-utils/wall.c
diff options
context:
space:
mode:
authorJim Patterson2016-11-16 18:22:14 +0100
committerKarel Zak2016-12-02 13:53:03 +0100
commit01544c52f86166a4a5dc81835027e412580396b2 (patch)
treea154d85fb04a29e8c87bcafdbf96a539468c001e /term-utils/wall.c
parentlsns: Fix parser for /proc/<pid>/stat which is including space in comm (diff)
downloadkernel-qcow2-util-linux-01544c52f86166a4a5dc81835027e412580396b2.tar.gz
kernel-qcow2-util-linux-01544c52f86166a4a5dc81835027e412580396b2.tar.xz
kernel-qcow2-util-linux-01544c52f86166a4a5dc81835027e412580396b2.zip
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@iki.fi> for an initial skeleton implementation. [kzak@redhat.com: - rename max to ngroups - add free_group_workspace() - some cosmetic changes] Reference: http://www.ibm.com/support/knowledgecenter/ssw_aix_61/com.ibm.aix.cmds6/wall.htm Signed-off-by: Jim Patterson <jimp@wegrok.net> Signed-off-by: Karel Zak <kzak@redhat.com>
Diffstat (limited to 'term-utils/wall.c')
-rw-r--r--term-utils/wall.c76
1 files changed, 75 insertions, 1 deletions
diff --git a/term-utils/wall.c b/term-utils/wall.c
index 4ad94d808..7eee88c90 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"
@@ -86,6 +89,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 +100,67 @@ static void __attribute__((__noreturn__)) usage(FILE *out)
exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
}
+struct group_workspace {
+ gid_t requested_group;
+ int ngroups;
+ gid_t *groups;
+};
+
+static gid_t get_group_gid(const char *optarg)
+{
+ struct group *gr;
+
+ if ((gr = getgrnam(optarg)))
+ return gr->gr_gid;
+ return strtou64_or_err(optarg, _("invalid group argument"));
+}
+
+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->ngroups = sysconf(_SC_NGROUPS_MAX) + 1; /* room for the primary gid */
+ buf->groups = xcalloc(sizeof(gid_t), buf->ngroups);
+
+ return buf;
+}
+
+static void free_group_workspace(struct group_workspace *buf)
+{
+ if (!buf)
+ return;
+
+ free(buf->groups);
+ free(buf);
+}
+
+static int is_gr_member(const char *login, const struct group_workspace *buf)
+{
+ struct passwd *pw;
+ int ngroups = buf->ngroups;
+ int rc;
+
+ pw = getpwnam(login);
+ if (buf->requested_group == pw->pw_gid)
+ return 1;
+
+ rc = getgrouplist(login, pw->pw_gid, buf->groups, &ngroups);
+ 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"));
+ }
+
+ for (; ngroups >= 0; --ngroups) {
+ if (buf->requested_group == buf->groups[ngroups])
+ return 1;
+ }
+
+ return 0;
+}
+
int main(int argc, char **argv)
{
int ch;
@@ -104,6 +169,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 +179,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 +190,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 +203,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 +242,16 @@ 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);
+ free_group_workspace(group_buf);
exit(EXIT_SUCCESS);
}