]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
wall: add --group option
authorJim Patterson <jimp@wegrok.net>
Wed, 16 Nov 2016 17:22:14 +0000 (12:22 -0500)
committerKarel Zak <kzak@redhat.com>
Fri, 2 Dec 2016 12:53:03 +0000 (13:53 +0100)
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>
bash-completion/wall
term-utils/wall.1
term-utils/wall.c

index e3145ff6d7e94fca141ebbc968356cab357d4db9..1331e6aebb4a777297af1c6fbdac9d23063c1b36 100644 (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
                        ;;
index 33cd35208b86a88aa12a0b9d97a1538e83bac675..a7da1951ed6cea5d723499b9c923c4408e7caf2d 100644 (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
index 4ad94d808889b40074387fa6bb2222880e5ca9e6..7eee88c9028545b57936fddd42843c8ba9cf4bdc 100644 (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);
 }