4.2) How do I check to see if there are characters to be read without actually reading?
4.3) How do I find the name of an open file?
4.4) How can an executing program determine its own pathname?
4.5) How do I use popen() to open a process for reading AND writing?
4.6) How do I sleep() in a C program for less than one second?
4.7) How can I get setuid shell scripts to work?
4.9) How do I keep track of people who are fingering me?
Check out cbreak mode in BSD, ~ICANON mode in SysV.
If you don't want to tackle setting the terminal parameters yourself (using the "ioctl(2)" system call) you can let the stty program do the work - but this is slow and inefficient, and you should change the code to do it right some time:
#include
Several people have sent me various more correct solutions to this problem. I'm sorry that I'm not including any of them here, because they really are beyond the scope of this list.
You might like to check out the documentation for the "curses" library of portable screen functions. Often if you're interested in single-character I/O like this, you're also interested in doing some sort of screen display control, and the curses library provides various portable routines for both functions.
Certain versions of UNIX provide ways to check whether characters are currently available to be read from a file descriptor. In BSD, you can use select(2). You can also use the FIONREAD ioctl, which returns the number of characters waiting to be read, but only works on terminals, pipes and sockets. In System V Release 3, you can use poll(2), but that only works on streams. In Xenix - and therefore Unix SysV r3.2 and later - the rdchk() system call reports whether a read() call on a given file descriptor will block.
There is no way to check whether characters are available to be read from a FILE pointer. (You could poke around inside stdio data structures to see if the input buffer is nonempty, but that wouldn't work since you'd have no way of knowing what will happen the next time you try to fill the buffer.)
Sometimes people ask this question with the intention of
writing
if (characters available from fd)
read(fd, buf, sizeof buf);
in order to get the effect of a nonblocking read.
This is not
the best way to do this, because it is possible that
characters
will be available when you test for availability, but will
no
longer be available when you call read. Instead, set the
O_NDELAY flag (which is also called FNDELAY under BSD) using
the
F_SETFL option of fcntl(2). Older systems (Version 7, 4.1
BSD)
don't have O_NDELAY; on these systems the closest you can
get to
a nonblocking read is to use alarm(2) to time out the read.
In general, this is too difficult. The file descriptor may be attached to a pipe or pty, in which case it has no name. It may be attached to a file that has been removed. It may have multiple names, due to either hard or symbolic links.
If you really need to do this, and be sure you think long and hard about it and have decided that you have no choice, you can use find with the -inum and possibly -xdev option, or you can use ncheck, or you can recreate the functionality of one of these within your program. Just realize that searching a 600 megabyte filesystem for a file that may not even exist is going to take some time.
Your program can look at argv[0]; if it begins with a "/", it is probably the absolute pathname to your program, otherwise your program can look at every directory named in the environment variable PATH and try to find the first one that contains an executable file whose name matches your program's argv[0] (which by convention is the name of the file being executed). By concatenating that directory and the value of argv[0] you'd probably have the right name.
You can't really be sure though, since it is quite legal for one program to exec() another with any value of argv[0] it desires. It is merely a convention that new programs are exec'd with the executable file name in argv[0].
For instance, purely a hypothetical example:
#include
The executed program thinks its name (its argv[0] value) is "vi Thesis". (Certain other programs might also think that the name of the program you're currently running is "vi Thesis", but of course this is just a hypothetical example, don't try it yourself :-)
The problem with trying to pipe both input and output to an arbitrary slave process is that deadlock can occur, if both processes are waiting for not-yet-generated input at the same time. Deadlock can be avoided only by having BOTH sides follow a strict deadlock-free protocol, but since that requires cooperation from the processes it is inappropriate for a popen()-like library function. The 'expect' distribution includes a library of functions that a C programmer can call directly. One of the functions does the equivalent of a popen for both reading and writing. It uses ptys rather than pipes, and has no deadlock problem. It's portable to both BSD and SV. See the next answer for more about 'expect'.
4) make a general `setuid script server' that tries to locate the requested `service' in a database of valid scripts and upon success will start the right interpreter with the right arguments.
---------
Now that we have made sure the right file gets interpreted, are there any risks left?
Certainly! For shell scripts you must not forget to set the PATH variable to a safe path explicitly. Can you figure out why? Also there is the IFS variable that might cause trouble if not set properly. Other environment variables might turn out to compromise security as well, e.g. SHELL... Furthermore you must make sure the commands in the script do not allow interactive shell escapes! Then there is the umask which may have been set to something strange...
Etcetera. You should realise that a setuid script `inherits' all the bugs and security risks of the commands that it calls!
All in all we get the impression setuid shell scripts are quite a risky business! You may be better off writing a C program instead!
Use fuser (system V), fstat (BSD), ofiles (public domain) or pff (public domain). These programs will tell you various things about processes using particular files.
A port of the 4.3 BSD fstat to Dynix, SunOS and Ultrix can be found in archives of comp.sources.unix, volume 18.
pff is part of the kstuff package, and works on quite a few systems. Instructions for obtaining kstuff are provided in question 3.10.
I've been informed that there is also a program called lsof. I don't know where it can be obtained.
Michael Fink Michael.Fink@uibk.ac.at adds:
If you are unable to unmount a file system for which above tools do not report any open files make sure that the file system that you are trying to unmount does not contain any active mount points (df(1)).
Generally, you can't find out the userid of someone who is fingering you from a remote machine. You may be able to find out which machine the remote request is coming from. One possibility, if your system supports it and assuming the finger daemon doesn't object, is to make your .plan file a "named pipe" instead of a plain file. (Use 'mknod' to do this.)
You can then start up a program that will open your .plan file for writing; the open will block until some other process (namely fingerd) opens the .plan for reading. Now you can feed whatever you want through this pipe, which lets you show different .plan information every time someone fingers you. One program for doing this is the "planner" package in volume 41 of the comp.sources.misc archives.
Of course, this may not work at all if your system doesn't support named pipes or if your local fingerd insists on having plain .plan files.
Your program can also take the opportunity to look at the output of "netstat" and spot where an incoming finger connection is coming from, but this won't get you the remote user.
Getting the remote userid would require that the remote site be running an identity service such as RFC 931. There are now three RFC 931 implementations for popular BSD machines, and several applications (such as the wuarchive ftpd) supporting the server. For more information join the rfc931-users mailing list, rfc931-users-request@kramden.acf.nyu.edu.
There are three caveats relating to this answer. The first is that many NFS systems won't recognize the named pipe correctly. This means that trying to read the pipe on another machine will either block until it times out, or see it as a zero-length file, and never print it.
The second problem is that on many systems, fingerd checks that the .plan file contains data (and is readable) before trying to read it. This will cause remote fingers to miss your .plan file entirely.
The third problem is that a system that supports named pipes usually has a fixed number of named pipes available on the system at any given time - check the kernel config file and FIFOCNT option. If the number of pipes on the system exceeds the FIFOCNT value, the system blocks new pipes until somebody frees the resources. The reason for this is that buffers are allocated in a non-paged memory.
Most variants of Unix do not support "detaching" and "attaching" processes, as operating systems such as VMS and Multics support. However, there are three freely redistributable packages which can be used to start processes in such a way that they can be later reattached to a terminal.
The first is "screen," which is described in the comp.sources.unix archives as "Screen, multiple windows on a CRT" (see the "screen-3.2" package in comp.sources.misc, volume 28.) This package will run on at least BSD, System V r3.2 and SCO UNIX.
The second is "pty," which is described in the comp.sources.unix archives as a package to "Run a program under a pty session" (see "pty" in volume 23). pty is designed for use under BSD-like system only.
The third is "dislocate," which is a script that comes with the expect distribution. Unlike the previous two, this should run on all UNIX versions. Details on getting expect can be found in question 3.9 .
None of these packages is retroactive, i.e. you must have started a process under screen or pty in order to be able to detach and reattach it.
There are a few different ways you can do this, although none of them is perfect:
* kibitz allows two (or more) people to interact with a shell (or any arbitary program). Uses include:
- watching or aiding another person's terminal session;
- recording a conversation while retaining the ability to
scroll backwards, save the conversation, or even edit it
while in progress;
- teaming up on games, document editing, or other cooperative
tasks where each person has strengths and weakness that
complement one another.
kibitz comes as part of the expect distribution. See question 3.9.
kibitz requires permission from the person to be spyed upon. To
spy without permission requires less pleasant approaches:
* You can write a program that rummages through Kernel structures and watches the output buffer for the terminal in question, displaying characters as they are output. This, obviously, is not something that should be attempted by anyone who does not have experience working with the Unix kernel. Furthermore, whatever method you come up with will probably be quite non-portable.
* If you want to do this to a particular hard-wired terminal all the time (e.g. if you want operators to be able to check the console terminal of a machine from other machines), you can actually splice a monitor into the cable for the terminal. For example, plug the monitor output into another machine's serial port, and run a program on that port that stores its input somewhere and then transmits it out *another* port, this one really going to the physical terminal. If you do this, you have to make sure that any output from the terminal is transmitted back over the wire, although if you splice only into the computer->terminal wires, this isn't much of a problem. This is not something that should be attempted by anyone who is not very familiar with terminal wiring and such.
* The latest version of screen includes a multi-user mode. Some details about screen can be found in question 4.10.
* If the system being used has streams (SunOS, SVR4), the advise program that was posted in volume 28 of comp.sources.misc can be used. AND it doesn't requirethat it be run first (you do have to configure your system in advance to automatically push the advise module on the stream whenever a tty or pty is opened).