#!/usr/bin/perl
#
# This is a sample implementation of a UID/GID GPFS remapping helper 
# application pair, provided for testing and illustrative purposes.  It uses 
# the Full Name (a.k.a. gecos) field in /etc/passwd as the globally unique user
# name.  No name-based GID remapping is done in this implementation.  When 
# remapping for the purposes of credentials checking (intent is 'credentials'),
# we do remapping for the UID based on the symbolic name, and replace the 
# entire list of GIDs with the GIDs of the user on the home cluster.

$debug = 0;

if ($#ARGV != 3)
{
  die("Usage: mmname2uid domain intent nUids nGids\n");
}

$domain = $ARGV[0];
$intent = $ARGV[1];
$nUids = $ARGV[2];
$nGids = $ARGV[3];

# Do basic argument sanity checking
if ( ($intent ne "credentials") && ($intent ne "stat") && ($intent ne "acl") )
{
  die("Invalid intent value: $intent\n");
}

if ( ($intent eq "credentials") && ($nUids == 0) )
{
  die("The number of UIDs to be remapped cannot be zero\n");
}

if ( ($intent eq "credentials") && ($nUids != 1) )
{
  die("Only one UID is allowed for credentials remapping\n");
}
 
# In this sample implementation, we replace the entire list of GIDs
# with a new list when remapping for credentials checking.  The matching 
# mmuid2name never outputs any GIDs, so we should not be expecting any 
# (a different implementation may handle this differently)
if ($intent eq "credentials")
{
  $nGids = 0;
}

# Read the list of UIDs and GIDs.  Note that the interface conventions 
# stipulate that when the intent is 'stat', two lines of input will be
# provided for each ID: symbolic name and original numeric ID.
for ($i = 0; $i < $nUids; $i++)
{
  $name = <STDIN>;
  print(STDERR $name) if $debug;
  chop($name);
  $gcoss{$name} = $i;
  $uids[$i] = "UNKNOWN USER";
  if ($intent eq "stat")
  {
    $id = <STDIN>;
    print(STDERR $id) if $debug;
    chop($id);
    $orig_uids[$i] = $id;
  }
}
for ($i = 0; $i < $nGids; $i++)
{
  $gname = <STDIN>;
  print(STDERR $gname) if $debug;
  chop($gname);
  $gnames{$gname} = $i;
  if ($intent eq "stat")
  {
    $id = <STDIN>;
    print(STDERR $id) if $debug;
    chop($id);
    $orig_gids[$i] = $id;
  }
}

# Go though the list of users, as returned by getpwent (normally it's the
# list from /etc/passwd or NIS), and find users who have their Full Name
# matching to one of the input names.
$nFound = 0;
while(true)
{
  @pwent = getpwent();
  if ($#pwent == -1){ last; }
  $username = $pwent[0];
  $uid = $pwent[2];
  $gid = $pwent[3];
  $gcos = $pwent[6];
  if (exists $gcoss{$gcos})
  {
    $uids[$gcoss{$gcos}] = $uid;
    if ($intent eq "credentials")
    {
      $gids[$gcoss{$gcos}] = $gid;
      $usernames[$gcoss{$gcos}] = $username;
    }
    $nFound++;
    last if $nFound >= $nUids;
  }
}
endpwent();

# Get UID/GID of 'nobody'.  In this implementation, we return those when the
# symbolic name is not found in our /etc/passwd.
@a = getpwnam("nobody");
$nobody_uid = $a[2];
$nobody_gid = $a[3];

# In this implementation, the convention is to use "UNKNOWN USER" string
# to denote a name that has not been found.
for ($i = 0; $i < $nUids; $i++)
{
  # if the user is not found, print ids of nobody
  if ($uids[$i] eq "UNKNOWN USER")
  {
    printf("%u\n", $nobody_uid);
    printf("%u\n", $nobody_gid) if ($intent eq "credentials");
    next;
  }
 
  printf("%u\n", $uids[$i]);
  # if we are remapping for credentials checking, get a list of groups
  # that the user belongs to
  if ($intent eq "credentials")
  {
    # primary gid
    printf("%u\n", $gids[$i]);
    # supplementary groups
    while(true)
    {
      ($name, $passwd, $gid, $members) = getgrent();
      last if $name eq "";
      # skip group if it's user's primary group
      next if $gid == $gids[$i];
      @memberlist = split(' ', $members);
      foreach(@memberlist)
      {
        if ($_ eq $usernames[$i])
        {
          printf("%u\n", $gid);
          last;
        }
      }
    }
  }
}

# In this example, we don't do remapping for gids for stat.  We choose not 
# to remap gids at all, i.e. return the same gid that was supplied to us.  An 
# alternative would be to return gid of 'nobody' for all gids, but that is
# hardly useful.
for ($i = 0; $i < $nGids; $i++)
{
  printf("%u\n", $orig_gids[$i]);
}
