Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

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.

share|improve this question
quick note: I'm not so sure now about the different setsid ordering being the cause. Apparently I'm bringing some SysV-isms to BSD, which doesn't give you a controlling terminal just when you open it anyway. Still stuck though. – Nicholas Wilson Mar 31 '12 at 0:47
You can answer to your own question, accept it and then ask a new one. – Coren Apr 8 '12 at 21:30
I can do better! I've got a new method. – Nicholas Wilson Apr 9 '12 at 15:53
up vote 1 down vote accepted

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;
share|improve this answer

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

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