/**
 * A main() is provided so that quick testing of this
 * library can be done. 
 */
int main(int argc, char **argv) {
  char **groupname;
  struct hadoop_group_info *ginfo;
  int ret, reentrant  = !getenv("NONREENTRANT");
  
  if (!reentrant) {
    fprintf(stderr, "testing non-reentrant...\n");
  }
  ginfo = hadoop_group_info_alloc(reentrant);
  if (!ginfo) {
    fprintf(stderr, "hadoop_group_info_alloc returned NULL.\n");
    return EXIT_FAILURE;
  }
  for (groupname = argv + 1; *groupname; groupname++) {
    gid_t gid = atoi(*groupname);
    if (gid == 0) {
      fprintf(stderr, "won't accept non-parseable group-name or gid 0: %s\n",
              *groupname);
      return EXIT_FAILURE;
    }
    ret = hadoop_group_info_fetch(ginfo, gid);
    if (!ret) {
      fprintf(stderr, "gid[%lld] : gr_name = %s\n",
              (long long)gid, ginfo->group.gr_name);
    } else {
      fprintf(stderr, "group[%lld] : error %d (%s)\n",
              (long long)gid, ret, strerror(ret));
    }
  }
  hadoop_group_info_free(ginfo);
  return EXIT_SUCCESS;
}
JNIEXPORT jobjectArray JNICALL 
Java_org_apache_hadoop_security_JniBasedUnixGroupsMapping_getGroupsForUser 
(JNIEnv *env, jclass clazz, jstring jusername)
{
  const char *username = NULL;
  struct hadoop_user_info *uinfo = NULL;
  struct hadoop_group_info *ginfo = NULL;
  jstring jgroupname = NULL;
  int i, ret, nvalid;
  int pw_lock_locked = 0;
  jobjectArray jgroups = NULL, jnewgroups = NULL;

  if (pw_lock_object != NULL) {
    if ((*env)->MonitorEnter(env, pw_lock_object) != JNI_OK) {
      goto done; // exception thrown
    }
    pw_lock_locked = 1;
  }
  username = (*env)->GetStringUTFChars(env, jusername, NULL);
  if (username == NULL) {
    goto done; // exception thrown
  }
  uinfo = hadoop_user_info_alloc();
  if (!uinfo) {
    THROW(env, "java/lang/OutOfMemoryError", NULL);
    goto done;
  }
  ret = hadoop_user_info_fetch(uinfo, username);
  if (ret == ENOENT) {
    jgroups = (*env)->NewObjectArray(env, 0, g_string_clazz, NULL);
    goto done;
  }
  ginfo = hadoop_group_info_alloc();
  if (!ginfo) {
    THROW(env, "java/lang/OutOfMemoryError", NULL);
    goto done;
  }
  ret = hadoop_user_info_getgroups(uinfo);
  if (ret) {
    if (ret == ENOMEM) {
      THROW(env, "java/lang/OutOfMemoryError", NULL);
    } else {
      char buf[128];
      snprintf(buf, sizeof(buf), "getgrouplist error %d (%s)",
               ret, terror(ret));
      THROW(env, "java/lang/RuntimeException", buf);
    }
    goto done;
  }
  jgroups = (jobjectArray)(*env)->NewObjectArray(env, uinfo->num_gids,
                                                 g_string_clazz, NULL);
  for (nvalid = 0, i = 0; i < uinfo->num_gids; i++) {
    ret = hadoop_group_info_fetch(ginfo, uinfo->gids[i]);
    if (ret) {
      logError(env, clazz, uinfo->gids[i], ret);
    } else {
      jgroupname = (*env)->NewStringUTF(env, ginfo->group.gr_name);
      if (!jgroupname) { // exception raised
        (*env)->DeleteLocalRef(env, jgroups);
        jgroups = NULL;
        goto done;
      }
      (*env)->SetObjectArrayElement(env, jgroups, nvalid++, jgroupname);
      // We delete the local reference once the element is in the array.
      // This is OK because the array has a reference to it.
      // Technically JNI only mandates that the JVM allow up to 16 local
      // references at a time  (though many JVMs allow more than that.)
      (*env)->DeleteLocalRef(env, jgroupname);
    }
  }
  if (nvalid != uinfo->num_gids) {
    // If some group names could not be looked up, allocate a smaller array
    // with just the entries that could be resolved.  Java has no equivalent to
    // realloc, so we have to do this manually.
    jnewgroups = (jobjectArray)(*env)->NewObjectArray(env, nvalid,
            (*env)->FindClass(env, "java/lang/String"), NULL);
    if (!jnewgroups) { // exception raised
      (*env)->DeleteLocalRef(env, jgroups);
      jgroups = NULL;
      goto done;
    }
    for (i = 0; i < nvalid; i++) {
      jgroupname = (*env)->GetObjectArrayElement(env, jgroups, i);
      (*env)->SetObjectArrayElement(env, jnewgroups, i, jgroupname);
      (*env)->DeleteLocalRef(env, jgroupname);
    }
    (*env)->DeleteLocalRef(env, jgroups);
    jgroups = jnewgroups;
  }

done:
  if (pw_lock_locked) {
    (*env)->MonitorExit(env, pw_lock_object);
  }
  if (username) {
    (*env)->ReleaseStringUTFChars(env, jusername, username);
  }
  if (uinfo) {
    hadoop_user_info_free(uinfo);
  }
  if (ginfo) {
    hadoop_group_info_free(ginfo);
  }
  if (jgroupname) {
    (*env)->DeleteLocalRef(env, jgroupname);
  }
  return jgroups;
}