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>
This commit is contained in:
Jim Patterson 2016-11-16 12:22:14 -05:00 committed by Karel Zak
parent 3fcbd79789
commit 01544c52f8
3 changed files with 86 additions and 1 deletions

View File

@ -9,6 +9,10 @@ _wall_module()
COMPREPLY=( $(compgen -W "seconds" -- $cur) )
return 0
;;
'-g'|'--group')
COMPREPLY=( $(compgen -A 'group' -- $cur) )
return 0
;;
'-h'|'--help'|'-V'|'--version')
return 0
;;

View File

@ -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

View File

@ -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);
}