1

I'm using the ioctl syscall on Linux to get the current tty in a C program.

I don't know why the ioctl fails if the program is run manually (as root) while it works as expected if the init.d script is used (as root). Note that I tried using either an SSH connection or the terminal application in the GUI session. The only way I managed to make the program running correctly is by switching to a virtual terminal (CTRL+Fx where x is a number) and run the command from there. I tested the program on RHEL8 and RHEL6. On RHEL8 the ioctl returns ENOTTY ("Inappropriate ioctl for device") while on RHEL6 the error is EINVAL ("Invalid argument").

I wrote this test program to debug the issue but it seems to behave differently from the main program:

#include <stdlib.h> 
#include <stdio.h>
#include <string.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>

#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <linux/vt.h>
#include <linux/kd.h>

int main()
{

    long tty = -1;
    long fd = 0;
    int result = 0;
    struct vt_stat ttyinfo;

    fd = open("/dev/tty", O_RDWR | O_NOCTTY);

    if(fd <= 0) 
    {
        printf("Trying tty0\n");
        fd = open("/dev/tty0", O_RDWR | O_NOCTTY);

        if(fd <= 0)
        {
            printf("[open] error = %s\n" , strerror(errno) );
            return -1;
        }
    }

    result = ioctl(fd, VT_GETSTATE, &ttyinfo);

    if (result == 0) 
    {
        tty = ttyinfo.v_active;
        printf("tty: %d\n" , tty);
    }
    else
    {
        printf("[ioctl] error = %s\n" , strerror(errno) );
    }

    return 0;
}

NOTE: This is only a piece of code, the real program is daemonized and perform much more operations.

RHEL8:

If the command (real application) is run from the UI or SSH /dev/tty and /dev/tty0 are tried and the reported error is: Inappropriate ioctl for device.

RHEL6: If the command (real application) is run from the UI or SSH /dev/tty only is used and the reported error is: Invalid argument

Do you know why this is happening ? I tried to add the daemonization to the test program but while the test program keep working the main program don't. There must be something else that the main program is doing and the test program don't.

  • what about cat /sys/class/tty/tty0/active? btw, I cannot reproduce your problem, either on centos7 or rhel8. please post a complete example. – mosvy Oct 9 '19 at 10:27
  • I updated the code i figured out that the test program is working while the real application is failing. There must be something that I failed to report into the test program. – Bemipefe Oct 9 '19 at 14:32
  • @mosvy is the file /sys/class/tty/tty0/active present on all kernel versions ? The application was written years ago and I noticed that this file is not present on RHEL6. – Bemipefe Oct 9 '19 at 14:42
  • available since this commit (<2.6.39, 2011) – mosvy Oct 9 '19 at 15:08
3

I suspect this is happening because you’re running your program from a terminal which isn’t a virtual console. /dev/tty is a special device which provides access to the controlling terminal, and that’s not necessarily a virtual console; but the ioctl you’re using only works on virtual consoles.

Your program will work reliably if you ensure that fd points at a virtual console, and the easiest way to do that when running as root is to open /dev/tty0, which is the active virtual console:

#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/vt.h>

int main(int argc, char **argv) {
  long tty = -1;
  long fd = 0;
  int result = 0;
  struct vt_stat ttyinfo;

  fd = open("/dev/tty0", O_RDWR | O_NOCTTY);
  if (fd < 0) {
    perror("Error opening /dev/tty0");
    return 1;
  }
  result = ioctl(fd, VT_GETSTATE, &ttyinfo);
  if (result == 0) {
    tty = ttyinfo.v_active;
    printf("Active tty: %ld\n", tty);
  } else {
    perror("ioctl failed");
  }
}
| improve this answer | |
  • Thanks @StephenKitt . The program worked as expected when I run it from a virtual terminal (I switched from the UI terminal to a console terminal with CTRL+F3). I tried to switch to tty0 but the problem was the same, maybe the error was different but the program wasn't working. – Bemipefe Oct 9 '19 at 10:02
  • Could you clarify what the error was? I’ve added the program I used to verify my answer. – Stephen Kitt Oct 9 '19 at 10:04
  • I updated the question with such information. The errors and the tty devices were different in the OS used to run the test program – Bemipefe Oct 9 '19 at 14:28
  • 1
    Your program fails because you’re only trying /dev/tty0 if you fail to open /dev/tty; but the latter won’t fail. To determine the active VT, you need to skip /dev/tty entirely and open /dev/tty0. – Stephen Kitt Oct 9 '19 at 14:34
  • I don't know why the code was written in this way. Does the tty terminal have a specific usage ? Do you have good links on this topic ? I don't know much about virtual terminals. – Bemipefe Oct 9 '19 at 14:52

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.