]> git.ipfire.org Git - thirdparty/open-vm-tools.git/commitdiff
Support for VMODL Guest Operations inside a FreeBSD Guest OS.
authorVMware, Inc <>
Mon, 22 Aug 2011 20:02:44 +0000 (13:02 -0700)
committerMarcelo Vanzin <mvanzin@vmware.com>
Mon, 22 Aug 2011 20:02:44 +0000 (13:02 -0700)
The following changeset reflects support for the GuestProcessManager APIs only.
(GuestFileManager APIs will be worked upon next).

. Enable some Posix_ & ProcMgr_ functions for FreeBSD

. Enable Impersonate functions for Start, Stop, GetUserInfo for FreeBSD

. Write new function for Listing Processes using libkvm (procfs is not auto mounted in FreeBSD for security reasons)

. Use the original process envp pointer (from the AppToolsContext) in VixTools_Initialize() to build the user env hash table. This was suggested by Marcelo over trying to use the "environ" global variable, since "environ" might reflect additional env changes done by the loader scripts, that should not be returned to the VixClient user.

Signed-off-by: Marcelo Vanzin <mvanzin@vmware.com>
open-vm-tools/lib/auth/authPosix.c
open-vm-tools/lib/misc/posixDlopen.c
open-vm-tools/lib/procMgr/procMgrPosix.c
open-vm-tools/services/plugins/vix/foundryToolsDaemon.c
open-vm-tools/services/plugins/vix/vixTools.c
open-vm-tools/services/plugins/vix/vixToolsEnvVars.c
open-vm-tools/services/plugins/vix/vixToolsInt.h

index fc3f44915aa552c523bd9e8459f7ff7714d6ec01..379aa8eff6b301f5d65b50dad60034dddd2ab773 100644 (file)
@@ -46,6 +46,8 @@
 #ifdef USE_PAM
 #if defined(sun)
 #define CURRENT_PAM_LIBRARY    "libpam.so.1"
+#elif defined(__FreeBSD__)
+#define CURRENT_PAM_LIBRARY    "libpam.so"
 #else
 #define CURRENT_PAM_LIBRARY    "libpam.so.0"
 #endif
index 1885e8f029a8c18060b71d349f5a017840b157b1..a0159d3fcf61d82c6322355904632b2b913f133a 100644 (file)
@@ -21,7 +21,7 @@
 #include <stdlib.h>
 #include <errno.h>
 
-#if defined(__APPLE__) || defined(linux) || defined(sun)
+#if !defined(_WIN32)
 #include <dlfcn.h>
 #endif
 
@@ -29,7 +29,7 @@
 #include "posixInt.h"
 
 
-#if defined(__APPLE__) || defined(linux) || defined(sun)
+#if !defined(_WIN32)
 /*
  *----------------------------------------------------------------------
  *
index 860a5821b7a4756c09037deee5a9b86bc40e1dfd..65e0179da78efd79a3c2c9d73c5ce15bea2755ee 100644 (file)
@@ -33,6 +33,8 @@
 #include <unistd.h>
 #if !defined(__FreeBSD__) && !defined(sun) && !defined(__APPLE__)
 #include <asm/param.h>
+#endif
+#if !defined(sun) && !defined(__APPLE__)
 #include <locale.h>
 #include <sys/stat.h>
 #endif
 #include <time.h>
 #include <grp.h>
 #include <sys/syscall.h>
-#if defined(linux) || defined(HAVE_SYS_USER_H)
+#if defined(linux) || defined(__FreeBSD__) || defined(HAVE_SYS_USER_H)
 // sys/param.h is required on FreeBSD before sys/user.h
 #   include <sys/param.h>
 // Pull in PAGE_SIZE/PAGE_SHIFT defines ahead of vm_basic_defs.h
 #   include <sys/user.h>
 #endif
-
+#if defined (__FreeBSD__)
+#include <kvm.h>
+#include <sys/sysctl.h>
+#include <paths.h>
+#endif
 #include "vmware.h"
 #include "procMgr.h"
 #include "vm_assert.h"
@@ -171,13 +177,13 @@ setresgid(gid_t ruid,
  *----------------------------------------------------------------------
  */
 
-#if !defined(sun)
+#if !defined(sun) && !defined(__FreeBSD__)
 int
 ProcMgr_ReadProcFile(int fd,                       // IN
                      char **contents)              // OUT
 {
    int size = 0;
-#if !defined(__FreeBSD__) && !defined(__APPLE__)
+#if !defined(__APPLE__)
    char tmp[512];
    int numRead;
 
@@ -227,7 +233,7 @@ done:
    return size;
 }
 
-#endif   // !sun
+#endif   // !sun && !FreeBSD
 
 /*
  *----------------------------------------------------------------------
@@ -247,12 +253,12 @@ done:
  *----------------------------------------------------------------------
  */
 
-#if !defined(sun)
+#if !defined(sun) && !defined(__FreeBSD__)
 ProcMgr_ProcList *
 ProcMgr_ListProcesses(void)
 {
    ProcMgr_ProcList *procList = NULL;
-#if !defined(__FreeBSD__) && !defined(__APPLE__)
+#if !defined(__APPLE__)
    Bool failed = FALSE;
    DynBuf dbProcId;
    DynBuf dbProcCmd;
@@ -595,12 +601,211 @@ abort:
       ProcMgr_FreeProcList(procList);
       procList = NULL;
    }
-#endif // !defined(__FreeBSD__) && !defined(__APPLE__)
+#endif // !defined(__APPLE__)
 
    return procList;
 }
-#endif // !defined(sun)
+#endif // !defined(sun) & !defined(__FreeBSD__)
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ProcMgr_ListProcesses --
+ *
+ *      List all the processes that the calling client has privilege to
+ *      enumerate. The strings in the returned structure should be all
+ *      UTF-8 encoded, although we do not enforce it right now.
+ *
+ * Results:
+ *
+ *      A ProcMgr_ProcList.
+ *
+ * Side effects:
+ *
+ *----------------------------------------------------------------------
+ */
+
+#if defined(__FreeBSD__)
+ProcMgr_ProcList *
+ProcMgr_ListProcesses(void)
+{
+   ProcMgr_ProcList *procList = NULL;
+   Bool failed = TRUE;
+   static kvm_t *kd;
+   struct kinfo_proc *kp;
+   char errbuf[_POSIX2_LINE_MAX];
+   int i, nentries=-1, flag=0;
+   DynBuf dbProcId;
+   DynBuf dbProcCmd;
+   DynBuf dbProcStartTime;
+   DynBuf dbProcOwner;
+
+   DynBuf_Init(&dbProcId);
+   DynBuf_Init(&dbProcCmd);
+   DynBuf_Init(&dbProcStartTime);
+   DynBuf_Init(&dbProcOwner);
+
+   /*
+    * Get the handle to the Kernel Virtual Memory
+    */
+   kd = kvm_openfiles(NULL, _PATH_DEVNULL, NULL, O_RDONLY, errbuf);
+   if (kd == NULL) {
+      Warning("%s: failed to open kvm with error: %s\n", __FUNCTION__, errbuf);
+      goto abort;
+   }
+
+   /*
+    * Get the list of process info structs
+    */
+   kp = kvm_getprocs(kd, KERN_PROC_PROC, flag, &nentries);
+   if ((kp == NULL && nentries > 0) || (kp != NULL && nentries < 0)) {
+      Warning("%s: failed to get proc infos with error: %s\n",
+              __FUNCTION__, kvm_geterr(kd));
+      goto abort;
+   }
+
+   /*
+    * Iterate through the list of process entries
+    */
+   for (i = 0; i < nentries; ++i, ++kp) {
+      struct passwd *pwd;
+      char *userName = NULL;
+      char *cmdLine = NULL;
+      char **cmdLineTemp = NULL;
+      time_t processStartTime;
+
+      /*
+       * Store the pid of the process
+       */
+      if (!DynBuf_Append(&dbProcId, &(kp->ki_pid), sizeof(kp->ki_pid))) {
+         Warning("%s: failed to append pid in DynBuf - out of memory\n",
+                 __FUNCTION__);
+         goto abort;
+      }
+
+      /*
+       * Store the owner of the process.
+       */
+      pwd = getpwuid(kp->ki_uid);
+      userName = (NULL == pwd)
+                 ? Str_SafeAsprintf(NULL, "%d", (int) kp->ki_uid)
+                 : Unicode_Alloc(pwd->pw_name, STRING_ENCODING_DEFAULT);
+      if (!DynBuf_Append(&dbProcOwner, &userName, sizeof userName)) {
+         Warning("%s: failed to append username in DynBuf - out of memory\n",
+                 __FUNCTION__);
+         goto abort;
+      }
 
+      /*
+       * Store the command line string of the process
+       */
+      cmdLineTemp = kvm_getargv(kd, kp, 0);
+      if (cmdLineTemp != NULL) {
+         // flatten the argument list to get cmd & all params
+         DynBuf dbuf;
+
+         DynBuf_Init(&dbuf);
+         while (*cmdLineTemp != NULL) {
+            if (!DynBuf_Append(&dbuf, *cmdLineTemp, strlen(*cmdLineTemp))) {
+               Warning("%s: failed to append cmd/args in DynBuf - no memory\n",
+                       __FUNCTION__);
+               goto abort;
+            }
+            cmdLineTemp++;
+            if (*cmdLineTemp != NULL) {
+               // add the whitespace between arguments
+               if (!DynBuf_Append(&dbuf, " ", 1)) {
+                  Warning("%s: failed to append ' ' in DynBuf - no memory\n",
+                          __FUNCTION__);
+                  goto abort;
+               }
+            }
+         }
+         // add the NUL term
+         if (!DynBuf_Append(&dbuf, "", 1)) {
+            Warning("%s: failed to append NUL in DynBuf - out of memory\n",
+                    __FUNCTION__);
+            goto abort;
+         }
+         DynBuf_Trim(&dbuf);
+         cmdLine = DynBuf_Detach(&dbuf);
+         DynBuf_Destroy(&dbuf);
+      } else {
+         cmdLine = Unicode_Alloc(kp->ki_comm, STRING_ENCODING_DEFAULT);
+      }
+      if (!DynBuf_Append(&dbProcCmd, &cmdLine, sizeof cmdLine)) {
+         Warning("%s: failed to append cmdline in DynBuf - out of memory\n",
+                 __FUNCTION__);
+         goto abort;
+      }
+
+      /*
+       * Store the start time of the process
+       */
+      processStartTime = kp->ki_start.tv_sec;
+      if (!DynBuf_Append(&dbProcStartTime,
+                    &processStartTime,
+                    sizeof processStartTime)) {
+         Warning("%s: failed to append start time in DynBuf - out of memory\n",
+                 __FUNCTION__);
+         goto abort;
+      }
+
+   } // for nentries
+
+   if (0 == DynBuf_GetSize(&dbProcId)) {
+      goto abort;
+   }
+
+   /*
+    * We're done adding to DynBuf.  Trim off any unused allocated space.
+    * DynBuf_Trim() followed by DynBuf_Detach() avoids a memcpy().
+    */
+   DynBuf_Trim(&dbProcId);
+   DynBuf_Trim(&dbProcCmd);
+   DynBuf_Trim(&dbProcStartTime);
+   DynBuf_Trim(&dbProcOwner);
+
+   /*
+    * Create a ProcMgr_ProcList and populate its fields.
+    */
+   procList = (ProcMgr_ProcList *) Util_SafeCalloc(1, sizeof(ProcMgr_ProcList));
+   ASSERT_NOT_IMPLEMENTED(procList);
+
+   procList->procCount = DynBuf_GetSize(&dbProcId) / sizeof(pid_t);
+
+   procList->procIdList = (pid_t *) DynBuf_Detach(&dbProcId);
+   ASSERT_NOT_IMPLEMENTED(procList->procIdList);
+   procList->procCmdList = (char **) DynBuf_Detach(&dbProcCmd);
+   ASSERT_NOT_IMPLEMENTED(procList->procCmdList);
+   procList->startTime = (time_t *) DynBuf_Detach(&dbProcStartTime);
+   ASSERT_NOT_IMPLEMENTED(procList->startTime);
+   procList->procOwnerList = (char **) DynBuf_Detach(&dbProcOwner);
+   ASSERT_NOT_IMPLEMENTED(procList->procOwnerList);
+
+   failed = FALSE;
+abort:
+   DynBuf_Destroy(&dbProcId);
+   DynBuf_Destroy(&dbProcCmd);
+   DynBuf_Destroy(&dbProcStartTime);
+   DynBuf_Destroy(&dbProcOwner);
+
+   if (kd != NULL) {
+      /*
+       * Wrap up: also deallocates memory (if) allocated by kvm_getargv()
+       */
+      kvm_close(kd);
+   }
+
+   if (failed) {
+      ProcMgr_FreeProcList(procList);
+      procList = NULL;
+   }
+
+   return procList;
+}
+#endif // defined(__FreeBSD__)
 
 /*
  *----------------------------------------------------------------------
@@ -1435,7 +1640,7 @@ ProcMgr_Free(ProcMgr_AsyncProc *asyncProc) // IN
    free(asyncProc);
 }
 
-#ifdef linux
+#if defined(linux) || defined(__FreeBSD__)
 
 /*
  *----------------------------------------------------------------------
@@ -1634,4 +1839,4 @@ ProcMgr_GetImpersonatedUserInfo(char **userName,            // OUT
    return TRUE;
 }
 
-#endif // linux
+#endif // linux || __FreeBSD__
index 2ca72b42f6f5b8458597c3f27ec734f9d90cc664..efdbbfa84793a6ae4005f641246c0e268cd27e45 100644 (file)
@@ -80,8 +80,8 @@
 #include "hgfsDevLinux.h"
 #endif
 
-/* Only Win32 and Linux use impersonation functions. */
-#if !defined(__FreeBSD__) && !defined(sun)
+/* Only Win32, Linux, Solaris and FreeBSD use impersonation functions. */
+#if !defined(__APPLE__)
 #include "impersonate.h"
 #endif
 
@@ -441,11 +441,15 @@ FoundryToolsDaemon_Initialize(ToolsAppCtx *ctx)
     * changed them.
     */
    (void) VixTools_Initialize(thisProcessRunsAsRoot,
-                              NULL,   // envp
+#if defined(__FreeBSD__)
+                              ctx->envp,   // envp
+#else
+                              NULL,        // envp
+#endif
                               ToolsDaemonTcloReportProgramCompleted,
                               ctx);
 
-#if defined(linux) || defined(_WIN32)
+#if !defined(__APPLE__)
    if (thisProcessRunsAsRoot) {
       Impersonate_Init();
    }
index 89cc5fc52ccc92d4fe725abed4a671ba1b082f51..ccf792808a315feec897e9232603cf0877f8405e 100644 (file)
@@ -496,7 +496,7 @@ static VixError VixToolsImpersonateUserImplEx(char const *credentialTypeStr,
                                               char const *obfuscatedNamePassword,
                                               void **userToken);
 
-#if defined(_WIN32) || defined(linux) || defined(sun)
+#if !defined(__APPLE__)
 static VixError VixToolsDoesUsernameMatchCurrentUser(const char *username);
 #endif
 
@@ -1275,7 +1275,7 @@ VixToolsRunProgramImpl(char *requestName,      // IN
    si.dwFlags = STARTF_USESHOWWINDOW;
    si.wShowWindow = (VIX_RUNPROGRAM_ACTIVATE_WINDOW & runProgramOptions)
                      ? SW_SHOWNORMAL : SW_MINIMIZE;
-#else
+#elif !defined(__FreeBSD__)
    procArgs.envp = VixToolsEnvironmentTableToEnvp(userEnvironmentTable);
 #endif
 
@@ -1438,7 +1438,7 @@ VixToolsStartProgramImpl(const char *requestName,            // IN
     * For non-Windows, we use the user's $HOME if workingDir isn't supplied.
     */
    if (NULL == workingDir) {
-#if defined(linux) || defined(sun)
+#if defined(linux) || defined(sun) || defined(__FreeBSD__)
       char *username = NULL;
 
       if (!ProcMgr_GetImpersonatedUserInfo(&username, &workingDirectory)) {
@@ -2355,7 +2355,7 @@ static Bool
 VixToolsComputeEnabledProperty(GKeyFile *confDictRef,            // IN
                                const char *varName)              // IN
 {
-#if defined(_WIN32) || defined(linux) || defined(sun)
+#if !defined(__APPLE__)
    return VixToolsGetAPIDisabledFromConf(confDictRef, varName);
 #else
    return FALSE;
@@ -3458,6 +3458,14 @@ VixToolsGetMultipleEnvVarsForUser(void *userToken,       // IN
    char *resultLocal = Util_SafeStrdup("");  // makes the loop cleaner.
    VixToolsUserEnvironment *env;
 
+#ifdef __FreeBSD__
+   if (NULL == userEnvironmentTable) {
+      err = VIX_E_FAIL;
+      free(resultLocal);
+      return err;
+   }
+#endif
+
    err = VixToolsNewUserEnvironment(userToken, &env);
    if (VIX_FAILED(err)) {
       env = NULL;
@@ -3467,7 +3475,29 @@ VixToolsGetMultipleEnvVarsForUser(void *userToken,       // IN
    for (i = 0; i < numNames; i++) {
       char *value;
 
+#ifdef __FreeBSD__
+      /*
+       * We should check the original envp for all vars except
+       * a few whitelisted ones that we set/unset on impersonate
+       * user start/stop. for them we need to do getenv()
+       */
+      if (!strcmp(names, "USER") ||
+          !strcmp(names, "HOME") ||
+          !strcmp(names, "SHELL")) {
+         value = VixToolsGetEnvFromUserEnvironment(env, names);
+      }
+      else {
+         if (HashTable_Lookup(userEnvironmentTable,
+                              names, (void **) &value)) {
+            value = Util_SafeStrdup(value);
+         } else {
+            value = Util_SafeStrdup("");
+         }
+      }
+#else
       value = VixToolsGetEnvFromUserEnvironment(env, names);
+#endif
+
       if (NULL != value) {
          char *tmp = resultLocal;
          char *tmpVal;
@@ -3544,6 +3574,14 @@ VixToolsGetAllEnvVarsForUser(void *userToken,     // IN
    char *resultLocal;
    VixToolsEnvIterator *itr;
    char *envVar;
+#ifdef __FreeBSD__
+   char **envp;
+   if (NULL == userEnvironmentTable) {
+      err = VIX_E_FAIL;
+      return err;
+   }
+   envp = VixToolsEnvironmentTableToEnvp(userEnvironmentTable);
+#endif
 
    if (NULL == result) {
       err = VIX_E_FAIL;
@@ -3552,7 +3590,11 @@ VixToolsGetAllEnvVarsForUser(void *userToken,     // IN
 
    resultLocal = Util_SafeStrdup("");  // makes the loop cleaner.
 
+#ifdef __FreeBSD__
+   err = VixToolsNewEnvIterator(userToken, envp, &itr);
+#else
    err = VixToolsNewEnvIterator(userToken, &itr);
+#endif
    if (VIX_FAILED(err)) {
       goto abort;
    }
@@ -3560,7 +3602,42 @@ VixToolsGetAllEnvVarsForUser(void *userToken,     // IN
    while ((envVar = VixToolsGetNextEnvVar(itr)) != NULL) {
       char *tmp = resultLocal;
       char *tmpVal;
+#ifdef __FreeBSD__
+      /*
+       * For variables we change during Impersonatation of user,
+       * we need to fetch from getenv() system call, all else
+       * can be read from the hash table of the original envp.
+       */
+      if (StrUtil_StartsWith(envVar, "USER=") ||
+          StrUtil_StartsWith(envVar, "HOME=") ||
+          StrUtil_StartsWith(envVar, "SHELL=")) {
+         char *name = NULL;
+         char *escapedName = NULL;
+         char *whereToSplit;
+         size_t nameLen;
+
+         whereToSplit = strchr(envVar, '=');
+         if (NULL == whereToSplit) {
+            /* Our code generated this list, so this shouldn't happen. */
+            ASSERT(0);
+            continue;
+         }
+
+         nameLen = whereToSplit - envVar;
+         name = Util_SafeMalloc(nameLen + 1);
+         memcpy(name, envVar, nameLen);
+         name[nameLen] = '\0';
+
+         escapedName = VixToolsEscapeXMLString(name);
 
+         free(envVar);
+         envVar = Str_SafeAsprintf(NULL, "%s=%s",
+                                   escapedName, Posix_Getenv(name));
+
+         free(name);
+         free(escapedName);
+      }
+#endif
       tmpVal = VixToolsEscapeXMLString(envVar);
       free(envVar);
       if (NULL == tmpVal) {
@@ -3581,6 +3658,9 @@ VixToolsGetAllEnvVarsForUser(void *userToken,     // IN
 
 abort:
    VixToolsDestroyEnvIterator(itr);
+#ifdef __FreeBSD__
+   VixToolsFreeEnvp(envp);
+#endif
    *result = resultLocal;
 
    return err;
@@ -6819,10 +6899,13 @@ VixToolsImpersonateUserImplEx(char const *credentialTypeStr,         // IN
    *userToken = NULL;
 
 ///////////////////////////////////////////////////////////////////////
-#if defined(__FreeBSD__)
-   err = VIX_E_NOT_SUPPORTED;
+// NOTE: The following 3 lines need to be uncommented to disable FreeBSD
+// support for VMODL Guest Operations completely - THE KILL SWITCH
+//#if defined(__FreeBSD__)
+//   err = VIX_E_NOT_SUPPORTED;
+//#endif
 ///////////////////////////////////////////////////////////////////////
-#elif defined(_WIN32) || defined(linux) || defined(sun)
+#if !defined(__APPLE__)
    {
       AuthToken authToken;
       char *unobfuscatedUserName = NULL;
@@ -7001,7 +7084,7 @@ VixToolsUnimpersonateUser(void *userToken)
    if (PROCESS_CREATOR_USER_TOKEN != userToken) {
 #if defined(_WIN32)
       Impersonate_Undo();
-#elif defined(linux) || defined(sun)
+#elif defined(linux) || defined(sun) || defined(__FreeBSD__)
       ProcMgr_ImpersonateUserStop();
 #endif
    }
@@ -7029,12 +7112,10 @@ VixToolsLogoutUser(void *userToken)    // IN
       return;
    }
 
-#if !defined(__FreeBSD__)
    if (NULL != userToken) {
       AuthToken authToken = (AuthToken) userToken;
       Auth_CloseToken(authToken);
    }
-#endif
 } // VixToolsLogoutUser
 
 
@@ -7056,7 +7137,7 @@ VixToolsLogoutUser(void *userToken)    // IN
 static char *
 VixToolsGetImpersonatedUsername(void *userToken)
 {
-#if defined(_WIN32) || defined(linux) || defined(sun)
+#if !defined(__APPLE__)
    char *userName = NULL;
    char *homeDir = NULL;
 
@@ -8116,7 +8197,7 @@ abort:
 #endif
 
 
-#if defined(_WIN32) || defined(linux) || defined(sun)
+#if !defined(__APPLE__)
 /*
  *-----------------------------------------------------------------------------
  *
@@ -8323,7 +8404,7 @@ abort:
    
    return err;
 }
-#endif  /* #if defined(_WIN32) || defined(linux) || defined(sun) */
+#endif  /* #if !defined(__APPLE__) */
 
 
 /*
index 22452e52934bb78c0d07651cda32a3386920e920..6f63b9d24afec45948de8007987d5a989daef5df 100644 (file)
@@ -94,6 +94,9 @@ struct VixToolsUserEnvironment {
 
 VixError
 VixToolsNewEnvIterator(void *userToken,                  // IN
+#ifdef __FreeBSD__
+                       char **envp,                      // IN
+#endif
                        VixToolsEnvIterator **envItr)     // OUT
 {
    VixError err = VIX_OK;
@@ -132,14 +135,7 @@ VixToolsNewEnvIterator(void *userToken,                  // IN
 #elif defined(__APPLE__)
    it->environ = *_NSGetEnviron();
 #elif defined(__FreeBSD__)
-   /*
-    * Looking at /build/toolchain/bsd32/freebsd-6.3/usr/include/stand.h,
-    * environ is a pointer to a doubly linked list of structs. I guess they
-    * just want to be different. Anyway, this is something that needs
-    * work if we want to support FreeBSD.
-    */
-   err = VIX_E_NOT_SUPPORTED;
-   goto abort;
+   it->environ = envp;
 #else
    it->environ = environ;
 #endif
index 8128cd39ab93aa23293c43768950983bb07c73fb..d907ba402ed91f07c935eb3896cda9e199971dd7 100644 (file)
@@ -99,7 +99,11 @@ void VixToolsUnimpersonateUser(void *userToken);
 
 void VixToolsLogoutUser(void *userToken);
 
-VixError VixToolsNewEnvIterator(void *userToken, VixToolsEnvIterator **envItr);
+VixError VixToolsNewEnvIterator(void *userToken,
+#ifdef __FreeBSD__
+                                char **envp,
+#endif
+                                VixToolsEnvIterator **envItr);
 
 char *VixToolsGetNextEnvVar(VixToolsEnvIterator *envItr);