coreutils
[Top][All Lists]
Advanced

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

Re: [PATCH v2] id: add feature to accept multiple usernames


From: Achilles Gaikwad
Subject: Re: [PATCH v2] id: add feature to accept multiple usernames
Date: Sat, 18 Aug 2018 03:01:18 +0530

Hello,

Any thoughts about this?

Best,
- Achilles


On Tue, Jul 24, 2018 at 1:09 AM Achilles Gaikwad <address@hidden> wrote:
>
> Adds a feature to id command to accept multiple usernames and/or uid
> as arguments.
>
> $ id root nobody
> uid=0(root) gid=0(root) groups=0(root)
> uid=99(nobody) gid=99(nobody) groups=99(nobody)
>
> * src/id.c (main): make varibles opt_zero, just_group_list,
> just_group, use_real, just_user global to be used in a new
> function.
> (print_stuff): new function that will print user and group information
> for the specified USER.
> When using -G option delimit each record with two consequent NULs.
> Restructure the code in the file to have global variables followed by
> functions to make the file look pretty.
> * tests/id/zero.sh: Add test cases to check the usage of -z option with
> multiple users.
> * doc/coreutils.texi: add minor documentation change to reflect the
> number of inputs that may be given.
>
> Signed-off-by: Achilles Gaikwad <address@hidden>
> ---
>  doc/coreutils.texi |   2 +-
>  src/id.c           | 171 +++++++++++++++++++++++++--------------------
>  tests/id/zero.sh   |  34 +++++++++
>  3 files changed, 131 insertions(+), 76 deletions(-)
>
> diff --git a/doc/coreutils.texi b/doc/coreutils.texi
> index 10fd023d8..f2d58b46d 100644
> --- a/doc/coreutils.texi
> +++ b/doc/coreutils.texi
> @@ -15200,7 +15200,7 @@ logins, groups, and so forth.
>  running it if no user is specified.  Synopsis:
>
>  @example
> -id [@var{option}]@dots{} [@var{user}]
> +id [@var{option}]@dots{} [@var{user}]@dots{}
>  @end example
>
>  @var{user} can be either a user ID or a name, with name look-up
> diff --git a/src/id.c b/src/id.c
> index be0758059..e60803894 100644
> --- a/src/id.c
> +++ b/src/id.c
> @@ -43,10 +43,20 @@
>
>  /* If nonzero, output only the SELinux context.  */
>  static bool just_context = 0;
> -
> -static void print_user (uid_t uid);
> -static void print_full_info (const char *username);
> -
> +/* If true, delimit entries with NUL characters, not whitespace */
> +bool opt_zero = false;
> +/* If true, output the list of all group IDs. -G */
> +bool just_group_list = false;
> +/* If true, output only the group ID(s). -g */
> +bool just_group = false;
> +/* If true, output real UID/GID instead of default effective UID/GID. -r */
> +bool use_real = false;
> +/* If true, output only the user ID(s). -u */
> +bool just_user = false;
> +/* True unless errors have been encountered.  */
> +static bool ok = true;
> +/* If true, we are using multiple users. Terminate -G with double NUL. */
> +static bool multiple_users = false;
>  /* If true, output user/group name instead of ID number. -n */
>  static bool use_name = false;
>
> @@ -54,13 +64,14 @@ static bool use_name = false;
>  static uid_t ruid, euid;
>  static gid_t rgid, egid;
>
> -/* True unless errors have been encountered.  */
> -static bool ok = true;
> -
>  /* The SELinux context.  Start with a known invalid value so print_full_info
>     knows when 'context' has not been set to a meaningful value.  */
>  static char *context = NULL;
>
> +static void print_user (uid_t uid);
> +static void print_full_info (const char *username);
> +static void print_stuff(const char *pw_name);
> +
>  static struct option const longopts[] =
>  {
>    {"context", no_argument, NULL, 'Z'},
> @@ -82,9 +93,9 @@ usage (int status)
>      emit_try_help ();
>    else
>      {
> -      printf (_("Usage: %s [OPTION]... [USER]\n"), program_name);
> +      printf (_("Usage: %s [OPTION]... [USER]...\n"), program_name);
>        fputs (_("\
> -Print user and group information for the specified USER,\n\
> +Print user and group information for the each specified USER,\n\
>  or (when USER omitted) for the current user.\n\
>  \n"),
>               stdout);
> @@ -116,18 +127,8 @@ main (int argc, char **argv)
>    int optc;
>    int selinux_enabled = (is_selinux_enabled () > 0);
>    bool smack_enabled = is_smack_enabled ();
> -  bool opt_zero = false;
>    char *pw_name = NULL;
>
> -  /* If true, output the list of all group IDs. -G */
> -  bool just_group_list = false;
> -  /* If true, output only the group ID(s). -g */
> -  bool just_group = false;
> -  /* If true, output real UID/GID instead of default effective UID/GID. -r */
> -  bool use_real = false;
> -  /* If true, output only the user ID(s). -u */
> -  bool just_user = false;
> -
>    initialize_main (&argc, &argv);
>    set_program_name (argv[0]);
>    setlocale (LC_ALL, "");
> @@ -185,11 +186,6 @@ main (int argc, char **argv)
>      }
>
>    size_t n_ids = argc - optind;
> -  if (1 < n_ids)
> -    {
> -      error (0, 0, _("extra operand %s"), quote (argv[optind + 1]));
> -      usage (EXIT_FAILURE);
> -    }
>
>    if (n_ids && just_context)
>      die (EXIT_FAILURE, 0,
> @@ -228,29 +224,44 @@ main (int argc, char **argv)
>          die (EXIT_FAILURE, 0, _("can't get process context"));
>      }
>
> -  if (n_ids == 1)
> -    {
> -      struct passwd *pwd = NULL;
> -      const char *spec = argv[optind];
> -      /* Disallow an empty spec here as parse_user_spec() doesn't
> -         give an error for that as it seems it's a valid way to
> -         specify a noop or "reset special bits" depending on the system.  */
> -      if (*spec)
> -        {
> -          if (parse_user_spec (spec, &euid, NULL, NULL, NULL) == NULL)
> -            {
> -              /* parse_user_spec will only extract a numeric spec,
> -                 so we lookup that here to verify and also retrieve
> -                 the PW_NAME used subsequently in group lookup.  */
> -              pwd = getpwuid (euid);
> -            }
> -        }
> -      if (pwd == NULL)
> -        die (EXIT_FAILURE, 0, _("%s: no such user"), quote (spec));
> -      pw_name = xstrdup (pwd->pw_name);
> -      ruid = euid = pwd->pw_uid;
> -      rgid = egid = pwd->pw_gid;
> -    }
> +  if (n_ids >= 1)
> +  {
> +      multiple_users = n_ids > 1 ? true : false;
> +      /* Changing the value of n_ids to the last index in the array where we
> +         have the last possible user id. This helps us because we don't have
> +         to declare a different variable to keep a track of where the last 
> username
> +         lies in argv[]. */
> +      n_ids += optind;
> +      /* For each username/userid to get its pw_name field */
> +      for (; optind < n_ids; optind++)
> +      {
> +          struct passwd *pwd = NULL;
> +          const char *spec = argv[optind];
> +          /* Disallow an empty spec here as parse_user_spec() doesn't
> +             give an error for that as it seems it's a valid way to
> +             specify a noop or "reset special bits" depending on the system. 
>  */
> +          if (*spec) {
> +              if (parse_user_spec(spec, &euid, NULL, NULL, NULL) == NULL)
> +              {
> +                  /* parse_user_spec will only extract a numeric spec,
> +                     so we lookup that here to verify and also retrieve
> +                     the PW_NAME used subsequently in group lookup.  */
> +                  pwd = getpwuid(euid);
> +              }
> +          }
> +          if (pwd == NULL)
> +          {
> +            error (0, errno, _("%s: no such user"), quote (argv[optind]));
> +            ok &= false;
> +            continue;
> +          }
> +          pw_name = xstrdup(pwd->pw_name);
> +          ruid = euid = pwd->pw_uid;
> +          rgid = egid = pwd->pw_gid;
> +          print_stuff(pw_name);
> +          IF_LINT (free (pw_name));
> +      }
> +  }
>    else
>      {
>        /* POSIX says identification functions (getuid, getgid, and
> @@ -289,34 +300,10 @@ main (int argc, char **argv)
>            if (rgid == NO_GID && errno)
>              die (EXIT_FAILURE, errno, _("cannot get real GID"));
>          }
> +        print_stuff(pw_name);
> +        IF_LINT (free (pw_name));
>      }
>
> -  if (just_user)
> -    {
> -      print_user (use_real ? ruid : euid);
> -    }
> -  else if (just_group)
> -    {
> -      if (!print_group (use_real ? rgid : egid, use_name))
> -        ok = false;
> -    }
> -  else if (just_group_list)
> -    {
> -      if (!print_group_list (pw_name, ruid, rgid, egid, use_name,
> -                             opt_zero ? '\0' : ' '))
> -        ok = false;
> -    }
> -  else if (just_context)
> -    {
> -      fputs (context, stdout);
> -    }
> -  else
> -    {
> -      print_full_info (pw_name);
> -    }
> -  putchar (opt_zero ? '\0' : '\n');
> -
> -  IF_LINT (free (pw_name));
>    return ok ? EXIT_SUCCESS : EXIT_FAILURE;
>  }
>
> @@ -356,7 +343,7 @@ print_user (uid_t uid)
>          {
>            error (0, 0, _("cannot find name for user ID %s"),
>                   uidtostr (uid));
> -          ok = false;
> +          ok &= false;
>          }
>      }
>
> @@ -415,7 +402,7 @@ print_full_info (const char *username)
>                   quote (username));
>          else
>            error (0, errno, _("failed to get groups for the current 
> process"));
> -        ok = false;
> +        ok &= false;
>          return;
>        }
>
> @@ -438,3 +425,37 @@ print_full_info (const char *username)
>    if (context)
>      printf (_(" context=%s"), context);
>  }
> +
> +/* Print information about the user based on the arguments passed. */
> +
> +static void
> +print_stuff(const char *pw_name)
> +{
> +  if (just_user)
> +      print_user (use_real ? ruid : euid);
> +
> +  /* print_group and print_group_lists functions return true on successful
> +     execution but false if something goes wrong. We then AND this value with
> +     the current value of 'ok' because we want to know if one of the previous
> +     users faced a problem in these functions. This value of 'ok' is later 
> used
> +     to understand what status program should exit with. */
> +  else if (just_group)
> +      ok &= print_group (use_real ? rgid : egid, use_name);
> +  else if (just_group_list)
> +      ok &= print_group_list (pw_name, ruid, rgid, egid,
> +               use_name, opt_zero ? '\0' : ' ');
> +  else if (just_context)
> +      fputs (context, stdout);
> +  else
> +      print_full_info (pw_name);
> +  /* When printing records for more than 1 user, at the end of groups
> +     of each user terminate the record with two consequent NUL characters
> +     to make parsing and distinguishing between two records possible. */
> + if (opt_zero && just_group_list && multiple_users)
> +    {
> +      putchar('\0');
> +      putchar('\0');
> +    }
> +  else
> +      putchar (opt_zero ? '\0' : '\n');
> +}
> diff --git a/tests/id/zero.sh b/tests/id/zero.sh
> index f183e18f8..7a94edde1 100755
> --- a/tests/id/zero.sh
> +++ b/tests/id/zero.sh
> @@ -63,4 +63,38 @@ printf '\n' >> out || framework_failure_
>  tr '\0' ' ' < out > out2 || framework_failure_
>  compare exp out2 || fail=1
>
> +# multiuser testing with -z
> +# test if the options work, these tests should pass if the above tests
> +# do.
> +
> +for o in g gr u ur ; do
> +  for n in '' n ; do
> +    id -${o}${n}  $users >> tmp1 ||
> +      { test $? -ne 1 || test -z "$n" && fail=1; }
> +    id -${o}${n}z $users  > tmp2 ||
> +      { test $? -ne 1 || test -z "$n" && fail=1; }
> +    tr '\0' '\n' < tmp2 >> tmp3
> +  done
> +done
> +compare tmp1 tmp3 || fail=1
> +
> +# Seperate checks when we are testing for multiple users && -G.
> +# This is done because we terminate the records with two consequent
> +# NULs instead of the regular single NUL.
> +
> +for o in G Gr ; do
> +  for n in '' n ; do
> +    id -${o}${n}  $users >> gtmp1 ||
> +      { test $? -ne 1 || test -z "$n" && fail=1; }
> +    id -${o}${n}z $users  > gtmp2 ||
> +      { test $? -ne 1 || test -z "$n" && fail=1; }
> +    # we replace all NULs with spaces, the result we get is there are two
> +    # consequent spaces instead of two consequent NUL's, we pass this to sed
> +    # to replace more than 1 space with a newline. This is ideally where a 
> new
> +    # line should be. This should make the output similar to without -z.
> +    tr '\0' ' ' < gtmp2 | sed 's/ \+ /\n/g' >> gtmp3
> +  done
> +done
> +compare gtmp1 gtmp3 || fail=1
> +
>  Exit $fail
> --
> 2.17.1
>
>



reply via email to

[Prev in Thread] Current Thread [Next in Thread]