sysctl and KERN_PROC_CWD


I need to obtain Current Working Directory for terminal. Specifically tmux needs that in order to get pane_current_path command. Current code in tmux uses sysctl(3) with parameters : KERN_PROC and KERN_PROC_CWD. However, if you invoke this as non root user sysctl(3) returns EPERM error for given parameters. If this code is run as root it works as supposed.

This is code snippet from tmux source code (

[noparse]static char *
osdep_get_cwd_fallback(int fd)
   static char        wd[PATH_MAX];
   struct kinfo_file   *info = NULL;
   pid_t            pgrp;
   int            nrecords, i;

   if ((pgrp = tcgetpgrp(fd)) == -1)
       return (NULL);

   if ((info = kinfo_getfile(pgrp, &nrecords)) == NULL) //fails with EPER for non root user
       return (NULL);

   for (i = 0; i < nrecords; i++) {
       if (info[i].kf_fd == KF_FD_TYPE_CWD) {
           strlcpy(wd, info[i].kf_path, sizeof wd);
           return (wd);

   return (NULL);

char *
osdep_get_cwd(int fd)
   static struct kinfo_file info;
   static int fallback;
   int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_CWD, 0 };
   size_t len = sizeof info;

   if (fallback)
       return (osdep_get_cwd_fallback(fd));

   if ((name[3] = tcgetpgrp(fd)) == -1)
       return (NULL);

   if (sysctl(name, 4, &info, &len, NULL, 0) == -1) { //fails wiht EPERM for non root user
       if (errno == ENOENT) {
           fallback = 1;
           return (osdep_get_cwd_fallback(fd));
       return (NULL);
   return (info.kf_path);
#else /* !KERN_PROC_CWD */
char *
osdep_get_cwd(int fd)
   return (osdep_get_cwd_fallback(fd));
#endif /* KERN_PROC_CWD */[/noparse]

My question is:

Since kfile_getinfo fails as well, it means that KERN_PROC_FILEDESC does not work either as non root user.
Is there any way to call sysctl with KERN_PROC_CWD or KERN_PROC_FILEDESC as non root user?
Is there any other way to obtain current working directory for process group other than sysctl?

many thanks
Hello SirDice,
Thank you for your help. Unfortunately, getcwd won't work since it will return the directory where I have invoked tmux. What we need is the directory of process group, that is KERN_PROC_CWD.


lsof can provie CWD for given pid. However, when I download source code for LSOF 4.9 and compile it with disabled security, it also fails with EPERM. My OS is vanila kernel nothing is re-configured after the install.


argh! no magic, just time to go to bed :) lsof sets group id on execution, and lsof owner is root:kmem. So, this is a done deal, lsof just elevates its priviledges and gets job done. I give up on cwd for group PID :(
I cannot replicate it with FreeBSD 11.0-STABLE as of r316618. I ran a simple program with that code and gave it stdin, stdout and stderr for file descriptors.

Is there anything specific about the file descriptor you are providing it? Is it of a process not owned by the caller? Either permission requirements changed or something changed in tmux to bump against an existing requirement. How are you producing it?

I was able to produce it if I ssh to the same box and run it without the -t option to ssh. tcgetpgrp() does not return an error if there is no foreground process group as noted by the man page. That could be where it is really failing. It returns a 0 (zero) for me via ssh. From
     The tcgetpgrp() function returns the value of the process group ID of the
     foreground process group associated with the terminal device.  If there
     is no foreground process group, tcgetpgrp() returns an invalid process
Here is the system call in the kernel, but I do not seen any recent changes specific to it.

If worse comes to worse, there is always libprocstat/procstat, which is standard in base since FreeBSD 9. For example, procstat -f <pid> gives a lot of information about a process.
For example:
# procstat -f 70207
70207 tmux              text v r r-------   -       - -   -
70207 tmux              ctty v c rw------   -       - -   /dev/pts/0
70207 tmux               cwd v d r-------   -       - -   /home/scf
70207 tmux              root v d r-------   -       - -   /
70207 tmux                 0 v c rw---n--   4 66150768 -   /dev/pts/0
70207 tmux                 1 v c rw---n--   4 66150768 -   /dev/pts/0
70207 tmux                 2 v c rw---n--   4 66150768 -   /dev/pts/0
70207 tmux                 3 p - rw---n--   1       0 -   -
70207 tmux                 4 p - rw---n--   1       0 -   -
70207 tmux                 5 s - rw---n--   1       0 UDS /tmp/tmux-2442/default
Also, fstat(1) can give similar output.