How does the libc function isatty() work? - c

I searched the linux repo and couldn't find a definition for it, so I guess it's something that comes with C or something like that? I'm trying to understand how isatty is able to tell whether a file descriptor is a terminal or not, and what it even means to "be a terminal," in technical language.
I'm not really sure where to find its implementation, and if it's in assembly I won't really be able to follow along with it very easily.

The general strategy for implementing isatty is to attempt a tty-specific ioctl operation on the file descriptor, and check for ENOTTY error result. Traditionally, TCGETS, which is the backend for the tcgetattr function, is used, but this is slightly dangerous since the ioctl number for it on Linux clashes with legacy OSS sound devices, and if the file descriptor actually refers to a certain type of MIDI device, it will make changes to the device. In musl libc, we use TIOCGWINSZ, the "get window size" operation, whose number was not inadvertently reused for any other types of devices and which reliably fails with ENOTTY for non-tty devices.
In theory, you could do it with fstat and check the st_rdev field for the device major number, but this would require a hard-coded list of all device majors which are ttys, and would have broken when new types were added (e.g. USB-serial/ACM devices, uartlite devices, etc.).

isatty(3) is a library function (you won't find anything about in the linux kernel), and is usually implemented by calling tcgetattr(3) and checking its return value.
For example, in the GNU C library (glibc):
/* Return 1 if FD is a terminal, 0 if not. */
int
__isatty (int fd)
{
struct termios term;
return __tcgetattr (fd, &term) == 0;
}
tcgetattr(3) itself will resolve to some ioctl like TCGETA or TCGETS.
Notice that isatty(3) will also return true for a master side of a pseudo-tty, which isn't really a tty -- most tty related ops performed on it will actually apply to its slave side.
On linux, isatty(3) will also return true for /dev/console, which again, isn't a real tty (it cannot be made the controlling tty of a process).
On linux, you can obtain a list of all the tty drivers on your system with their major and minor numbers via cat /proc/tty/drivers. Of course, that only reflects the modules which have been loaded.

Related

How to open a serial port with read/write lines reversed?

I know how to open a serial port using 'open' function:
open("/dev/portname", flags)
But I want two programs to open this port but with reversed read/write lines. For example when program 2 writes something to the port, program 1 can read it.
If you're using a Unix-like operating system, and if you don't need full serial port semantics, named pipes can be quite useful for doing this sort of thing.
If you need more control, you could perhaps use a pair of pseudoterminals, with a third program running in the background shuttling characters between the master ends.
And do see the related question "Virtual Serial Port for Linux" that the StackOverflow machinery already found for you.
You cannot typically do that in software.
Such things are normally done by hardware, and that is what cross-over cables and "null-modem" cables are good for.

Find free port to Open Pseudo Terminal Using BSD

I want to create a Pseudo Terminal using the BSD API. My understanding is that the Unix 98 API will automatically find a free port with posix_openpt() but with BSD API I need to check/find a free port to connect to. Is this correct?
So I need to do something like this...?
int fd, index = 0;
char serial_port[11]; // = "/dev/ptyp0"
while (true) {
sprintf(serial_port, "/dev/ptyp%d", index);
fd = open(serial_port, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (fd > 0)
break;
index++;
}
Do you know of a Pseudo Terminal tutorial that uses BSD? There are many using Unix 98 API but not many if any BSD API ones.
Using the actual device path is problematic because the pathnames can differ from system to system. Most portable applications which use pseudo terminals long ago were changed to use one of the library functions written to abstract away from the device path.
There is more than one flavor of BSD to consider, e.g., FreeBSD, NetBSD, OpenBSD (and others of course, including OSX). xterm's configure script identifies features from each. All have openpty in the "util" library, providing its prototype in util.h
POSIX provides posix_openpt which combines the functionality of openpty with others to provide a simpler interface. However (though a documented interface), it is known to not work properly with OSX (see for example OSX 10.7.5 UTF-8 encoding over ssh as well as xterm's changelog).
Here are manpage links:
FreeBSD
NetBSD
OpenBSD
OSX

How to attach to device context in another driver at Linux

Example: I use sensor TMP421 which driver is linux/drivers/hwmon/tmp421.c. It will export to /sys/class/hwmon/hwon0/.... And user can use cat command to read the temperatures. But my is request: I want to read it at kernel space to control some thing when the temperature reach to MAX values (example).
So how I can attach to get the device context to use the function
tmp421_data *tmp421_update_device(struct device *dev)
to read the temperature ?
Or is there another way ?
I don't want to use the cat command at my code.
Thanks
Just do bus_find_device_by_name on an i2c_bus_type. It should give you the correct device pointer relatively easily. You will also have to change the tmp421_update_device function from "static" to "exported" (and move tmp421_data structure to an external header).
If you don't want to alter drivers not of your own, you can still try to emulate the approach sysfs takes when accessing device information. The sysfs dirent will be accessible to you at the dev->kobj.sd field and it's a fairly simple data structure.
Critically, you need to call put_device() on the received device handle after you've finished with it (otherwise you will end up with kernel lock-up down the line because of "unreleasable" objects). If you're using the kobj.sd accessor, then sysfs_get()/sysfs_put() on it will also be required.
As said in https://stackoverflow.com/a/4407051/196561 ("How to use sysfs inside kernel module?") by shodanex
it is a Bad Idea (tm)
with link http://www.linuxjournal.com/article/8110
Driving Me Nuts - Things You Never Should Do in the Kernel - From Issue #133 Linux Journal, 2005 By Greg Kroah-Hartman
The article says that it is possible to use sys_open and sys_read from modules to open and read files:
old_fs = get_fs();
set_fs(KERNEL_DS);
fd = sys_open(filename, O_RDONLY, 0);
if (fd >= 0) {
/* read the file here */
sys_close(fd);
}
set_fs(old_fs);
Don't know will it work with files in /sys or not.
And Greg warns us:
I Never Told You about This.
In conclusion, reading and writing a file from within the kernel is a bad, bad thing to do. Never do it. Ever.
Better way may be to learn inter module communications, possibly with modifying the hwmon/tmp421.c.

Getting exclusive access to a tty device from a root program on Linux

I have a program running as root on Linux, talking to a tty (actually an LCD implemented as a tty). The device for what it's worth is /dev/ttyUSB0. I'd like to have my program that writes to this device be able to have exclusive access to the device, so as to not have any interference from other instances of the program running at the same time.
I see that there's a ioctl option called TIOCEXCL which will prevent additonal open's of the device to wit "multiple open() calls to the same file will succeed unless the TIOCEXCL ioctl is issued. This will prevent additional opens except by root-owned processes." I tested this and it works just as advertised: if a non-root user tries to open /dev/ttyUSB0 (once I changed the permissions) then the open fails with something like "device busy" and if a root user tries to open it, it works.
What I ideally want is a way to this exclusive access to the tty to work for root users. So I'd have multiple root users using the program that writes to the LCD, but somehow their access to the LCD (tty) would be serialized. Apparently the TIOCEXCL ioctl option will not work for me since it doesn't stop root users from opening an already-opened tty device.
I guess there are a number of options here, but I am reaching out to all ya'll to see if you might have other thoughts or suggestions.
Maybe I'm missing something about using TIOCEXCL...
Maybe there's some other way via open() or ioctl() or what-not to get exclusive access.
If there was some way I could detect that some other process has the device open, I could just wait and retry. I know about lsof but I'm loath to invoke it from inside this program just to learn this. And there are race conditions with that. (Maybe I can get over that? :) )
I could implement locking like apparently used to be done to get exclusive access to tty devices.
Update 1:
Since the only program writing to the LCD device is mine, I am inclined to do something like the following (pseudo-code) to lock inside the code:
f = open("/dev/ttyUSB0", O_RDWR)
flock(f, LOCK_EX)
// do any ioctl's, etc.
// do any write's
// sleep a tad to not flash messages too fast on LCD
nanosleep({0, 250000000}, NULL)
flock(f, LOCK_UN)
close(f)
Perhaps this discussion on LKML: [TTY] exclusive mode question can help you!
i have some issue like you.
If you can hack you linux kernel, you can to do dirty hack like this.
In file
linux/drivers/char/tty_io.c
add new command to function
long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
...
switch (cmd) {
...
case 0x54FF:
return put_user(tty->count, (int __user *)p);
...
}
The value of tty->count is a count of current open descriptors.
In your application you can try this code
int fd, count, res;
fd = open("/dev/ttyS0", O_RDWR|O_NOCTTY|O_NONBLOCK);
if (fd >= 0)
{
count = 0;
res = ioctl(fd, 0x54FF, &count);
if (res>=0)
{
if (count > 1)
{
printf("Device already in use.\n")
close(fd);
fd = -1;
}
}
}
Answer: Root always has access. Always.
Perhaps if you said more about what else was grabbing the device, or what you fear might grab the device...
I would encourage you to look into UUCP locking. There should be a library on Linux that implements it, but if not, it's fairly easy to implement. I've used it extensively in similar situations where I don't want multiple instances of the same program to step on each other.
As a side note, perhaps you should rethink the architecture of your solution. The process that accesses the LCD/ttyUSB0 could act as a server and handle messages from the client processes that need to write to the LCD. This would require some form of IPC. It may be overkill for your project.
Keep in mind that any solution you come up with will only work if all processes that access the device conform to the protocol. If you are worried about a rogue process running as root then you may be stuck with hacking the kernel to get the solution you want.
I don't know if you have already found a solution or you just changed your design but here's a work around in the "roots land":
Create another file in your system with the same name of your port or something including it like ttyUSB0_islocked.
when you open the port have your process look for/create and open this file and write some info like it's process Id in it.
before opening the port in other programs check if this file exists and there's a process with the same process id within the file and then proceed if there's no such process other wise wait or exit.

From userspace, how can I tell if Linux's soft watchdog is configured with no way out?

I am writing a system monitor for Linux and want to include some watchdog functionality. In the kernel, you can configure the watchdog to keep going even if /dev/watchdog is closed. In other words, if my daemon exits normally and closes /dev/watchdog, the system would still re-boot 59 seconds later. That may or may not be desirable behavior for the user.
I need to make my daemon aware of this setting because it will influence how I handle SIGINT. If the setting is on, my daemon would need to (preferably) start an orderly shutdown on exit or (at least) warn the user that the system is going to reboot shortly.
Does anyone know of a method to obtain this setting from user space? I don't see anything in sysconf() to get the value. Likewise, I need to be able to tell if the software watchdog is enabled to begin with.
Edit:
Linux provides a very simple watchdog interface. A process can open /dev/watchdog , once the device is opened, the kernel will begin a 60 second count down to reboot unless some data is written to that file, in which case the clock re-sets.
Depending on how the kernel is configured, closing that file may or may not stop the countdown. From the documentation:
The watchdog can be stopped without
causing a reboot if the device
/dev/watchdog is closed correctly,
unless your kernel is compiled with
the CONFIG_WATCHDOG_NOWAYOUT option
enabled.
I need to be able to tell if CONFIG_WATCHDOG_NOWAYOUT was set from within a user space daemon, so that I can handle the shutdown of said daemon differently. In other words, if that setting is high, a simple:
# /etc/init.d/mydaemon stop
... would reboot the system in 59 seconds, because nothing is writing to /dev/watchdog any longer. So, if its set high, my handler for SIGINT needs to do additional things (i.e. warn the user at the least).
I can not find a way of obtaining this setting from user space :( Any help is appreciated.
AHA! After digging through the kernel's linux/watchdog.h and drivers/watchdog/softdog.c, I was able to determine the capabilities of the softdog ioctl() interface. Looking at the capabilities that it announces in struct watchdog_info:
static struct watchdog_info ident = {
.options = WDIOF_SETTIMEOUT |
WDIOF_KEEPALIVEPING |
WDIOF_MAGICCLOSE,
.firmware_version = 0,
.identity = "Software Watchdog",
};
It does support a magic close that (seems to) override CONFIG_WATCHDOG_NOWAYOUT. So, when terminating normally, I have to write a single char 'V' to /dev/watchdog then close it, and the timer will stop counting.
A simple ioctl() on a file descriptor to /dev/watchdog asking WDIOC_GETSUPPORT allows one to determine if this flag is set. Pseudo code:
int fd;
struct watchdog_info info;
fd = open("/dev/watchdog", O_WRONLY);
if (fd == -1) {
perror("open");
// abort, timer did not start - no additional concerns
}
if (ioctl(fd, WDIOC_GETSUPPORT, &info)) {
perror("ioctl");
// abort, but you probably started the timer! See below.
}
if (WDIOF_MAGICCLOSE & info.options) {
printf("Watchdog supports magic close char\n");
// You have started the timer here! Handle that appropriately.
}
When working with hardware watchdogs, you might want to open with O_NONBLOCK so ioctl() not open() blocks (hence detecting a busy card).
If WDIOF_MAGICCLOSE is not supported, one should just assume that the soft watchdog is configured with NOWAYOUT. Remember, just opening the device successfully starts the countdown. If all you're doing is probing to see if it supports magic close and it does, then magic close it. Otherwise, be sure to deal with the fact that you now have a running watchdog.
Unfortunately, there's no real way to know for sure without actually starting it, at least not that I could find.
a watchdog guards against hard-locking the system, either because of a software crash, or hardware failure.
what you need is a daemon monitoring daemon (dmd). check 'monit'
I think the watchdog device drivers are really intended for use on embedded platforms (or at least well controlled ones) where the developers will have control of which kernel is in use.
This could be considered to be an oversight, but I think it is not.
One other thing you could try, if the watchdog was built as a loadable module, unloading it will presumably abort the shutdown?

Resources