When X starts, it searches for the lowest unused VT, and attaches to it. My problem is that when there are several running X processes, I need to be able to identify which one is the currently active one.

This is a *BSD question, because on linux it's easy: X sets its controlling terminal to be ttyN, or, on very old distributions, it's specified on the commandline as vtN. So, I'm running a service and I see that the currently active VT is tty7, and there are two X servers running, it's easy to tell which one corresponds to the current terminal. (This is a reasonable case: perhaps the user used GNOME/KDE's 'switch user' functionality or ran two servers using startx.) An example application that might want to follow the active X server is x11vnc (which is forked from the software I'm developing).

On FreeBSD though, the controlling terminal does not tell you anything. When X is started from ttyv1, that remains the controlling terminal.


I've done due diligence and read the X code. After some hunting around, it's now clearer to me what's going on.

In lnx_init.c, the X server does setsid to make a fresh session for itself, then opens an fd to ttyN straight after to do a VT_ACTIVATE ioctl on it. Pretty standard; opening the fd to a terminal with no controlling process from a process with no controlling terminal associates the two, and the server keeps the fd open, so it's guaranteed that the terminal will remaining the controlling terminal for the X server.

Now, in bsd_init.c, opening the fd to the tty to be used as the framebuffer doesn't make it a controlling terminal (and in fact, with no setsid, BSD Xserver started from xinit on ttyv2 will keep ttyv2 as its ctty!).

Question further updated and cleaned up on 2012-04-09.


There is a more general way. On all platforms with virtual terminals, including linux and BSD, Xserver retains an open fd to the terminal it's running on. On linux, it remains a good solution to check for the controlling terminal of the X process to distinguish between multiple X processes (use the seventh field of /proc/<..>/stat). More generally though, look through the list of open fds of the X process, and it only needs some simple filtering to get out the terminal the Xserver is running on. (Unfortunately, getting the list of open fds is again platform-dependant...) For sysctl platforms like BSD, the code will look similar to this, plus some error handling:

int ttyByOpenFds(int curPid) {
    int ctl[4] = { CTL_KERN, KERN_PROC, KERN_PROC_FILEDESC, curPid };
    size_t sizeGuess = 50*sizeof(kinfo_file);
    char* buf = malloc(sizeGuess);
    int rv = sysctl(ctl, 4, buf, &sizeGuess, 0, 0);
    if (rv < 0 && errno == ESRCH) return 0;
    else if (rv < 0 && errno == ENOMEM) { /* try again */ }
    else if (rv < 0) throw SystemException("unexpected error getting args", errno);

    char* position = buf;
    while (position < buf + sizeGuess) {
      kinfo_file* kfp = reinterpret_cast<kinfo_file*>(position);
      position += kfp->kf_structsize;
      if (kfp->kf_type != KF_TYPE_VNODE) continue;
      if (kfp->kf_vnode_type != KF_VTYPE_VCHR) continue;
      if (kfp->kf_fd < 0) continue;
      char* name = devname(kfp->kf_un.kf_file.kf_file_rdev, S_IFCHR);
      if (!name) continue;
      unsigned int ttynum = 0;
      if (sscanf(name, "ttyv%u", &ttynum) != 1) continue;
      if (ttynum < 8 && kfp->kf_fd <= 2) continue; // stderr going to a console
      return ttynum;
    return 0;
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.