>From f8ee96fbbd577b3d6eb2e2a072936b71cac2b959 Mon Sep 17 00:00:00 2001 From: Bernhard Voelker Date: Thu, 19 Sep 2013 01:23:00 +0200 Subject: [PATCH] id: add -z, --zero option * src/group-list.h (print_group_list): Add a parameter for the separator of type char. * src/group-list.c (print_group_list): Likewise, and use it instead of a white space character to separate the group entries. * src/groups.c (main): Pass white space character to print_group_list(). * src/id.c (longopts): Add array element for the new long option. (usage): Document the new option. While at it, fix the alignment of the descriptions to match that of HELP_OPTION_DESCRIPTION. (main): Define the bool flag opt_zero indicating the use of the new option. In the getopt_long loop, handle it. Output an error diagnostic in the case the --zero option has been specified together with the default format. In the case of -gG, pass either a NUL or a white space character to print_group_list() - depending on the above new flag. Likewise change the printing of the final newline character: output a NUL instead if the --zero option has been specified. * doc/coreutils.texi (id invocation): Document the new option. While at it, move the @exitstatus macro down after the macro @primaryAndSupplementaryGroups in order to be consistent with other info pages. (groups invocation): Move @exitstatus down after the macro @primaryAndSupplementaryGroups here, too. * tests/misc/id-zero.sh: Add new test exercising the new option. * tests/local.mk (all_tests): Reference it. * NEWS (New features): Mention the new option. Fixes http://bugs.gnu.org/9987 --- NEWS | 3 +++ doc/coreutils.texi | 22 +++++++++++++++----- src/group-list.c | 6 +++--- src/group-list.h | 2 +- src/groups.c | 6 +++--- src/id.c | 36 ++++++++++++++++++++++---------- tests/local.mk | 1 + tests/misc/id-zero.sh | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 110 insertions(+), 23 deletions(-) create mode 100755 tests/misc/id-zero.sh diff --git a/NEWS b/NEWS index d26722d..5891def 100644 --- a/NEWS +++ b/NEWS @@ -44,6 +44,9 @@ GNU coreutils NEWS -*- outline -*- du accepts a new option: --inodes to show the number of inodes instead of the blocks used. + id accepts a new option: --zero (-z) to separate the output entries by + a NUL instead of a white space character. + id and ls with -Z report the SMACK security context where available. mkdir, mkfifo and mknod with -Z set the SMACK context where available. diff --git a/doc/coreutils.texi b/doc/coreutils.texi index 21216b4..327254b 100644 --- a/doc/coreutils.texi +++ b/doc/coreutils.texi @@ -14516,9 +14516,20 @@ Print only the security context of the current user. If SELinux is disabled then print a warning and set the exit status to 1. address@hidden table address@hidden -z address@hidden --zero address@hidden -z address@hidden --zero +Separate output items with NUL characters. +This option is not permitted when using the default format. address@hidden +Example: address@hidden +$ id -Gn --zero +users devs address@hidden example + address@hidden table @macro primaryAndSupplementaryGroups{cmd,arg} Primary and supplementary groups for a process are normally inherited @@ -14530,6 +14541,8 @@ database to be consulted afresh, and so will give a different result. @end macro @primaryAndSupplementaryGroups{id,user argument} address@hidden + @node logname invocation @section @command{logname}: Print current login name @@ -14587,13 +14600,12 @@ groups address@hidden@dots{} The group lists are equivalent to the output of the command @samp{id -Gn}. address@hidden,list of users} - The only options are @option{--help} and @option{--version}. @xref{Common options}. address@hidden address@hidden,list of users} address@hidden @node users invocation @section @command{users}: Print login names of users currently logged in diff --git a/src/group-list.c b/src/group-list.c index 7d4995b..a76ee00 100644 --- a/src/group-list.c +++ b/src/group-list.c @@ -35,7 +35,7 @@ extern bool print_group_list (const char *username, uid_t ruid, gid_t rgid, gid_t egid, - bool use_names) + bool use_names, char separator) { bool ok = true; struct passwd *pwd = NULL; @@ -52,7 +52,7 @@ print_group_list (const char *username, if (egid != rgid) { - putchar (' '); + putchar (separator); if (!print_group (egid, use_names)) ok = false; } @@ -79,7 +79,7 @@ print_group_list (const char *username, for (i = 0; i < n_groups; i++) if (groups[i] != rgid && groups[i] != egid) { - putchar (' '); + putchar (separator); if (!print_group (groups[i], use_names)) ok = false; } diff --git a/src/group-list.h b/src/group-list.h index 3fac887..573de1d 100644 --- a/src/group-list.h +++ b/src/group-list.h @@ -16,4 +16,4 @@ along with this program. If not, see . */ bool print_group (gid_t, bool); -bool print_group_list (const char *, uid_t, gid_t, gid_t, bool); +bool print_group_list (const char *, uid_t, gid_t, gid_t, bool, char); diff --git a/src/groups.c b/src/groups.c index 53332d5..d30c9fb 100644 --- a/src/groups.c +++ b/src/groups.c @@ -114,13 +114,13 @@ main (int argc, char **argv) if (rgid == NO_GID && errno) error (EXIT_FAILURE, errno, _("cannot get real GID")); - if (!print_group_list (NULL, ruid, rgid, egid, true)) + if (!print_group_list (NULL, ruid, rgid, egid, true, ' ')) ok = false; putchar ('\n'); } else { - /* At least one argument. Divulge the details of the specified users. */ + /* At least one argument. Divulge the details of the specified users. */ while (optind < argc) { struct passwd *pwd = getpwnam (argv[optind]); @@ -130,7 +130,7 @@ main (int argc, char **argv) rgid = egid = pwd->pw_gid; printf ("%s : ", argv[optind]); - if (!print_group_list (argv[optind++], ruid, rgid, egid, true)) + if (!print_group_list (argv[optind++], ruid, rgid, egid, true, ' ')) ok = false; putchar ('\n'); } diff --git a/src/id.c b/src/id.c index 3e7016f..587dd3b 100644 --- a/src/id.c +++ b/src/id.c @@ -61,6 +61,7 @@ static security_context_t context = NULL; static struct option const longopts[] = { + {"zero", no_argument, NULL, 'z'}, {"context", no_argument, NULL, 'Z'}, {"group", no_argument, NULL, 'g'}, {"groups", no_argument, NULL, 'G'}, @@ -83,14 +84,18 @@ usage (int status) fputs (_("\ Print user and group information for the specified USERNAME,\n\ or (when USERNAME omitted) for the current user.\n\ -\n\ - -a ignore, for compatibility with other versions\n\ - -Z, --context print only the security context of the current user\n\ - -g, --group print only the effective group ID\n\ - -G, --groups print all group IDs\n\ - -n, --name print a name instead of a number, for -ugG\n\ - -r, --real print the real ID instead of the effective ID, with -ugG\n\ - -u, --user print only the effective user ID\n\ +\n"), + stdout); + fputs (_("\ + -a ignore, for compatibility with other versions\n\ + -Z, --context print only the security context of the current user\n\ + -g, --group print only the effective group ID\n\ + -G, --groups print all group IDs\n\ + -n, --name print a name instead of a number, for -ugG\n\ + -r, --real print the real ID instead of the effective ID, with -ugG\n\ + -u, --user print only the effective user ID\n\ + -z, --zero separate entries by a NUL, not whitespace;\n\ + not permitted in default format\n\ "), stdout); fputs (HELP_OPTION_DESCRIPTION, stdout); fputs (VERSION_OPTION_DESCRIPTION, stdout); @@ -109,6 +114,7 @@ main (int argc, char **argv) int optc; int selinux_enabled = (is_selinux_enabled () > 0); bool smack_enabled = is_smack_enabled (); + bool opt_zero = false; /* If true, output the list of all group IDs. -G */ bool just_group_list = false; @@ -127,7 +133,7 @@ main (int argc, char **argv) atexit (close_stdout); - while ((optc = getopt_long (argc, argv, "agnruGZ", longopts, NULL)) != -1) + while ((optc = getopt_long (argc, argv, "agnruzGZ", longopts, NULL)) != -1) { switch (optc) { @@ -162,6 +168,9 @@ main (int argc, char **argv) case 'u': just_user = true; break; + case 'z': + opt_zero = true; + break; case 'G': just_group_list = true; break; @@ -193,6 +202,10 @@ main (int argc, char **argv) error (EXIT_FAILURE, 0, _("cannot print only names or real IDs in default format")); + if (default_format && opt_zero) + error (EXIT_FAILURE, 0, + _("option --zero not permitted in default format")); + /* If we are on a SELinux/SMACK-enabled kernel, no user is specified, and either --context is specified or none of (-u,-g,-G) is specified, and we're not in POSIXLY_CORRECT mode, get our context. Otherwise, @@ -269,7 +282,8 @@ main (int argc, char **argv) } else if (just_group_list) { - if (!print_group_list (argv[optind], ruid, rgid, egid, use_name)) + if (!print_group_list (argv[optind], ruid, rgid, egid, use_name, + opt_zero ? '\0' : ' ')) ok = false; } else if (just_context) @@ -280,7 +294,7 @@ main (int argc, char **argv) { print_full_info (argv[optind]); } - putchar ('\n'); + putchar (opt_zero ? '\0' : '\n'); exit (ok ? EXIT_SUCCESS : EXIT_FAILURE); } diff --git a/tests/local.mk b/tests/local.mk index b00ff59..8f76b23 100644 --- a/tests/local.mk +++ b/tests/local.mk @@ -277,6 +277,7 @@ all_tests = \ tests/misc/id-context.sh \ tests/misc/id-groups.sh \ tests/misc/id-setgid.sh \ + tests/misc/id-zero.sh \ tests/misc/md5sum.pl \ tests/misc/md5sum-bsd.sh \ tests/misc/md5sum-newline.pl \ diff --git a/tests/misc/id-zero.sh b/tests/misc/id-zero.sh new file mode 100755 index 0000000..15f6a53 --- /dev/null +++ b/tests/misc/id-zero.sh @@ -0,0 +1,57 @@ +#!/bin/sh +# Exercise "id --zero". + +# Copyright (C) 2013 Free Software Foundation, Inc. + +# This program 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 3 of the License, or +# (at your option) any later version. + +# This program 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. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src +print_ver_ id + +u="$( id -nu )" +id || fail=1 +id "$u" || fail=1 +id root || fail=1 + +# Create a nice list of users (ignoring errors) ... +getent passwd | head -n 100 | cut -d: -f1 > users +# ... and ensure it contains at least 'root', "$u", and an +# empty line for the tests without a user argument below. +printf "%s\n" root "$u" '' >> users || framework_failure_ + +# id(1) should refuse --zero in default format. +id --zero > out 2>err && fail=1 +compare /dev/null out || fail=1 +grep 'option --zero not permitted in default format' err || fail=1 + +# Exercise "id -z" with various options. +printf "\n" > exp || framework_failure_ +cp /dev/null out || framework_failure_ + +while read u ; do + for o in g gr G Gr u ur ; do + for n in "" n ; do + printf "%s: " "id -${o}${n}[z] $u" >> exp || framework_failure_ + printf "\n%s: " "id -${o}${n}[z] $u" >> out || framework_failure_ + id -${o}${n} $u >> exp || fail=1 + id -${o}${n}z $u > tmp || fail=1 + head -c-1 < tmp | tr '\0' ' ' >> out || framework_failure_ + done + done +done < users + +printf "\n" >> out || framework_failure_ +compare exp out || fail=1 + +Exit $fail -- 1.8.3.1