mirror of
https://github.com/PDP-10/its.git
synced 2026-01-11 23:53:12 +00:00
Binary-only compiler and library, plus documentation and include files for compiling new programs.
451 lines
17 KiB
Plaintext
Executable File
451 lines
17 KiB
Plaintext
Executable File
USYS.DOC - KCC Un*x System-call simulation
|
||
|
||
This file documents various things about the USYS library routines,
|
||
which are intended to provide simulation and support for Un*x system
|
||
call functions.
|
||
|
||
Specifications for the interfaces were taken from the March
|
||
1984 4.2BSD Unix Programmer's Manual (UPM), plus the 4.3BSD man pages,
|
||
and all code here works as described in those references unless
|
||
otherwise documented.
|
||
|
||
Implementors should also read the CODSYS.DOC file in the
|
||
source directory.
|
||
|
||
Contents:
|
||
Intro - listing format
|
||
Summary of USYS routines
|
||
Definitions
|
||
Individual Routine Descriptions (as needed)
|
||
|
||
Library function listing format:
|
||
|
||
Name Module Port-status Comments
|
||
(routine name) (source file) (see below)
|
||
|
||
Port-status code:
|
||
E = file #includes "c-env.h" for environment config.
|
||
<sys> - runs on the given sys, one of: T20,10X,T10,WAITS,ITS.
|
||
*10 = portable to all PDP-10 OS (T20,10X,T10,WAITS,ITS)
|
||
* = fully portable (either no OS-dependent stuff, or a
|
||
fully-portable conditional exists)
|
||
|
||
Comments:
|
||
"U" means the call is USYS_macro enclosed and cannot be
|
||
interrupted by signals.
|
||
"-" means it isn't enclosed
|
||
(this better be cuz interrupts don't affect the call!)
|
||
"I" means it is interruptible (i.e. can return EINTR error).
|
||
"IC" means interrupts may be continued within the call.
|
||
|
||
USYS function summary: Src: lib/usys/
|
||
|
||
Name Module Port-status Comments
|
||
|
||
access ACCESS T20,10X U 10X only partial.
|
||
alarm ALARM T20 U
|
||
brk SBRK *10 U
|
||
chdir CHDIR T20,10X U
|
||
chmod CHMOD T20,10X U
|
||
chown CHOWN T20,10X U
|
||
close CLOSE T20,10X,ITS U
|
||
creat OPEN T20,10X U
|
||
dup DUP *10 U
|
||
dup2 DUP *10 U
|
||
errno (data) URT *10 -
|
||
exec[lv][ep] FORK T20,10X U
|
||
exit EXIT *10 -
|
||
fchmod CHMOD T20,10X U
|
||
fchown CHOWN T20,10X U
|
||
fcntl FCNTL *10 U
|
||
fork FORKEX T20,10X U
|
||
forkexec FORKEX T20,10X U KCC-specific routine.
|
||
fstat STAT T20,10X U (also: xfstat)
|
||
ftime TIME *10 -
|
||
geteuid GETUID T20,10X U
|
||
getpid GETPID *10 U see format note.
|
||
gettimeofday TIME *10 -
|
||
getuid GETUID T20,10X U
|
||
gtty SGTTY T20 U
|
||
ioctl IOCTL T20 UIC Partial.
|
||
kill SIGVEC T20,10X U
|
||
lseek LSEEK T20,10X U
|
||
open OPEN T20,10X U (Uses BSD flags; mode not supported)
|
||
pause PAUSE *10 -I Always returns with EINTR
|
||
pipe PIPE T20 U (monitor must have PIP: device)
|
||
psignal PSIGNA *10 -
|
||
raise SIGVEC T20,10X U (ANSI function, not syscall)
|
||
read READ T20,10X U
|
||
rename RENAME T20,10X U
|
||
sbrk SBRK *10 U
|
||
sigblock SIGVEC T20,10X U
|
||
signal SIGNAL T20,10X U
|
||
sigpause SIGVEC T20,10X UI Always returns with EINTR
|
||
sigreturn SIGVEC T20,10X U
|
||
sigsetmask SIGVEC T20,10X U
|
||
sigstack SIGVEC T20,10X U
|
||
sigvec SIGVEC T20,10X U
|
||
sleep SLEEP *10 -I (returns no value)
|
||
stat STAT T20,10X U (also: xstat)
|
||
stty SGTTY T20 U
|
||
tell LSEEK T20,10X U
|
||
time TIME *10 - (also: tadl_xxx routines)
|
||
times TIMES *10 -
|
||
unlink UNLINK T20,10X,ITS U (10X doesn't expunge)
|
||
utime UTIME T20,10X U
|
||
vfork FORK T20,10X U
|
||
wait WAIT T20,10X UIC
|
||
write WRITE T20,10X,ITS UIC
|
||
_exit EXIT *10 U
|
||
_runtm URT *10 (internal) C programs start here.
|
||
_urtsud (data) URTSUD *10 (internal) Runtime startup defs.
|
||
|
||
DEFINITIONS:
|
||
|
||
The UPM introduction contains some definitions which provide
|
||
a convenient way to start describing how the KCC simulations differ from
|
||
Un*x; some concepts are supported and others are not.
|
||
|
||
Process ID (PID): Supported, see long description.
|
||
Parent Process ID: Supported, see long description.
|
||
|
||
Process Group ID: not implemented
|
||
TTY Group ID: not implemented
|
||
|
||
Real User ID: Supported.
|
||
The user ID on T20/10X is the "user number".
|
||
|
||
Real Group ID: not implemented
|
||
Effective UID, GID, and Access Groups: not implemented
|
||
Super-user: not implemented
|
||
Special Processes: not implemented
|
||
|
||
File Descriptor (FD): Supported.
|
||
Small non-negative integers in the range 0 to 63 inclusive.
|
||
FDs 0, 1, and 2 correspond to standard input, output, and error output.
|
||
On T20/10X these are initially set to the JFNs .PRIIN, .PRIOU, .CTTRM.
|
||
You can obtain the JFN for a FD by using the fcntl() call.
|
||
|
||
File Name: Supported.
|
||
On T20/10X, up to 39 chars per component. Must be 7-bit ASCII.
|
||
Can quote with ^V.
|
||
|
||
Path Name: Supported.
|
||
Un*x style / paths are permitted, where foo/ is taken to mean foo
|
||
is a subdirectory of the current directory.
|
||
|
||
If the monitor worked right, a filespec of the form "C:foo/bar.h" could
|
||
be turned into "C:<.foo>bar.h", assuming standard-type relative
|
||
directory fixes, and we would win. But no. Instead, we have to do
|
||
the work manually: the logical device is recursively expanded until
|
||
an end device-directory pair is found, at which point the file
|
||
lookup is tried. If it fails, the expansion/traversal continues.
|
||
|
||
Directory: Supported.
|
||
<.> and <..> work on some T20 systems (Stanford mods).
|
||
|
||
Root Directory and Current Working Directory:
|
||
A directory of the form "/foo" is taken to mean "<foo>".
|
||
The notion of a current working directory is supported.
|
||
|
||
File Access Permissions:
|
||
Each set of T20/10X owner, group, and world access bits
|
||
corresponds to a set of Un*x owner, group, and other bits.
|
||
Un*x T20/10X
|
||
04 040 Read access
|
||
02 020 Write access
|
||
01 010 Execute access
|
||
- 04 Append access
|
||
- 02 Directory listing access
|
||
- 01 -
|
||
Thus, a call such as "chmod(foo.bar, 0644)" will set the T20/10X
|
||
protection of "foo.bar" to 604040.
|
||
There is no way a user program can either read or set the last
|
||
three T20/10X protection bits, except by doing a CHFDB% itself.
|
||
Finally, there is no T20/10X counterpart to the set-UID or set-GID
|
||
bits.
|
||
|
||
Sockets and Address Families: not implemented (yet!)
|
||
|
||
ERRORS:
|
||
|
||
The global "errno" is set by all failing USYS calls.
|
||
The standard UN*X error numbers from <errno.h> are used where possible,
|
||
although a few T20-specific error codes have been defined. See <errno.h>
|
||
for details.
|
||
There is currently no easy way to find out what TOPS-20/TENEX
|
||
error (if any) caused errno to be set. The best one can do is find the
|
||
most recent error for the process with GETER%. Perhaps someday this will
|
||
be improved.
|
||
|
||
Exceptions:
|
||
ftime, time, gettimeofday, times
|
||
getpid
|
||
_exit (never returns)
|
||
|
||
PROCESS ID (PID): Long Description
|
||
|
||
PID values are generated by:
|
||
(1) getpid() - self process ID. This must not change over
|
||
the lifetime of the process!
|
||
(2) fork() - to identify the child process.
|
||
(3) wait() - to identify the child process that stopped.
|
||
This should match the value returned by fork().
|
||
|
||
PID values are used by:
|
||
(1) kill() - to send signals to self, child, or parent.
|
||
(It is rare to send them anywhere else.)
|
||
(2) Code that checks the return value of wait().
|
||
(3) Code that generates unique filenames, port numbers, or the like
|
||
which should not conflict with those of any other
|
||
active process.
|
||
|
||
For ITS, TOPS-10, and WAITS the PID is simply the job #, a
|
||
small positive integer; zero and negative PIDs will never be seen, as
|
||
job 0 is the monitor itself and no system can support 2**35 jobs.
|
||
|
||
However, TENEX (and hence TOPS-20) has never had a notion of a
|
||
unique process identifier, except internally inside the monitor; this
|
||
fork ID is simply not accessible to the user. Fork handles are all
|
||
relative, in an obscene attempt to prevent programs from referencing
|
||
any process they shouldn't. This makes it impossible to implement
|
||
getpid() in a straightforward way. The subterfuge I have resorted to
|
||
is as follows:
|
||
|
||
T20 PID = <IPCF PID>,,<frk #><job #>
|
||
<IPCF PID> - Left half of a PID generated by MUTIL% for process
|
||
This is guaranteed by system to be unique.
|
||
<frk #> - low 9 bits of relative fork handle, in 0777000.
|
||
<job #> - low 9 bits of job number, in 0777.
|
||
|
||
getpid() remembers the value generated on first call and
|
||
returns that thereafter. This satisfies the uniqueness and constancy
|
||
criterion, as well as being efficient.
|
||
fork() and wait() convert the relative fork handles from
|
||
CFORK% and GFRKS% to a child PID with a zero LH but with the other
|
||
fields set. Since relative fork handles are from 400000 to 400777, we
|
||
only need the low 9 bits.
|
||
fork() in the new child process copies the saved getpid()
|
||
value, if any; this is its parent's PID and may be used by kill(). The
|
||
saved value is then cleared so if the child calls getpid() it will generate
|
||
its own unique value.
|
||
kill() checks its PID argument first against the saved
|
||
getpid() value to see if a signal is being sent to itself. If not it then
|
||
sees whether it matches that of its parent (if any) and sends a signal
|
||
to .FHSUP if so. Otherwise, if the LH is 0 it assumes the signal is
|
||
being sent to a child, and generates the appropriate relative fork
|
||
handle from the 9 bits in the PID value. Note: There is no good way
|
||
to identify "miscellaneous" signals generated by another process (PSIs
|
||
on the "CHNmisc" channel); only those PSIs uniquely mapped to a single
|
||
signal can be successfully sent between processes.
|
||
|
||
This scheme fails only if PIDs are somehow passed from one
|
||
process to another either via pipe, file, or vfork() shared memory, since
|
||
the result of a child's getpid() won't match what its parent's fork()
|
||
returned. But this should practically never happen.
|
||
|
||
TENEX:
|
||
On TENEX, which doesn't have IPCF, we just use GFRKS% to
|
||
locate our fork within the job fork structure and hope the resulting
|
||
number, which we stick in the LH, doesn't change. At least TENEX
|
||
doesn't have extended addressing either so we can munch the GFRKS%
|
||
data on the stack.
|
||
|
||
OPEN() - Some I/O details:
|
||
|
||
The open() call has several additional flags which are intended to
|
||
help specify the proper actions on TOPS-20/TENEX systems, since the
|
||
defaults assumed by open() may not always be correct.
|
||
|
||
Standard BSD flags:
|
||
O_RDONLY open for reading only
|
||
O_WRONLY open for writing only
|
||
O_RDWR open for reading and writing
|
||
O_NDELAY do not block on open (not supported)
|
||
O_APPEND append on each write
|
||
O_CREAT create file if it does not exist
|
||
O_TRUNC truncate size to 0
|
||
O_EXCL error if create and file exists
|
||
|
||
KCC-specific flags (not portable)
|
||
O_BINARY Open in binary (9-bit byte) mode
|
||
O_CONVERTED Force LF-conversion
|
||
O_UNCONVERTED Force NO LF-conversion
|
||
O_BSIZE_7 Force 7-bit bytesize
|
||
O_BSIZE_8 Force 8-bit bytesize
|
||
O_BSIZE_9 Force 9-bit bytesize
|
||
|
||
TOPS-20 and TENEX specific flags
|
||
O_T20_WILD Allow wildcards on GTJFN%
|
||
O_T20_WROLD For writes, do NOT use GJ%FOU
|
||
O_T20_SYS_LOG logical device is system-wide!
|
||
O_T20_THAWED Open file for thawed access
|
||
|
||
The BSD flags behave as per the UPM documentation, and the T20
|
||
flags are fairly straightforward. The KCC flags however are more subtle;
|
||
they affect the characteristics of the I/O that will be done, rather than
|
||
how a file will be found or created. The two decisions that must be made
|
||
are: (1) Bytesize, and (2) LF-conversion. These are explained below.
|
||
|
||
BYTESIZE:
|
||
The decision of which bytesize to use for I/O is somewhat
|
||
complicated. The bytesize on UN*X is always 8 bits, but on PDP-10s it
|
||
can be anything from 0 to 36 bits. The algorithm we use is as follows:
|
||
If a byte size (one of 7, 8, or 9) is explicitly requested, use that.
|
||
Otherwise, for a new file, use 9 if O_BINARY, else 7.
|
||
for an old file, use the file's bytesize.
|
||
A size of 0 or 36 is treated as for a new file.
|
||
Any other size is simply used. If this is not
|
||
one of 7, 8, or 9 then the results are unpredictable.
|
||
|
||
LF-CONVERSION:
|
||
UN*X text files use the convention that a LF alone is a
|
||
"newline", whereas PDP-10 systems use CRLF together. Thus, the normal
|
||
mode of I/O uses LF-conversion, wherein read() converts a input CRLF
|
||
sequence to LF, and write() converts an output LF to CRLF. The algorithm
|
||
used to determine whether LF-conversion should be done is:
|
||
Conversion is normally only done if the bytesize is 7.
|
||
Any other size implies NO conversion.
|
||
However, this default can be overriden by certain flags:
|
||
If O_CONVERTED is set, conversion is ALWAYS done.
|
||
If O_UNCONVERTED or O_BINARY is set, conversion is NEVER done.
|
||
|
||
LSEEK() - Problems with LF-conversion
|
||
|
||
lseek() deals only with system-level file pointers. When no
|
||
LF-conversion is being done, this corresponds exactly to the UN*X notion
|
||
of a file position, namely the # of bytes offset from the start of the file,
|
||
and it is possible to create your own file positions arithmetically.
|
||
|
||
However, when LF-conversion is being done then this is not possible;
|
||
the position returned by lseek will correspond to the system's position,
|
||
rather than to the number of bytes fed through read() or write(). In this
|
||
case you can only lseek to a position previously returned by lseek() itself.
|
||
(Note that 0 is a special case that always works). Typically the pointer
|
||
returned will be larger than the number of bytes read or written thus far,
|
||
since the system is aware of the CR's in the file even though the C program
|
||
isn't.
|
||
|
||
STAT() - file status information
|
||
|
||
This section describes the correspondence between the components
|
||
of the stat() structure (as defined for Un*x) and the TOPS-20 file system
|
||
information.
|
||
|
||
struct stat
|
||
{
|
||
dev_t st_dev; /* The .DVxxx device type */
|
||
ino_t st_ino; /* .FBADR - Disk address of file index blk */
|
||
unsigned int st_mode; /* Un*x-style mode bits */
|
||
int st_nlink; /* 1 (always) */
|
||
int st_uid; /* T20: User #, 10X: directory # */
|
||
int st_gid; /* 0 (always, for now) */
|
||
dev_t st_rdev; /* 0 (always, for now) */
|
||
off_t st_size; /* .FBSIZ - size in bytes (any bytesize) */
|
||
time_t st_atime; /* .FBREF - last ref (Un*x format time) */
|
||
int st_spare1;
|
||
time_t st_mtime; /* .FBWRT - last write (Un*x format time) */
|
||
int st_spare2;
|
||
time_t st_ctime; /* .FBCRE - last mod (Un*x format time) */
|
||
int st_spare3;
|
||
long st_blksize; /* # bytes in a page (derived from FB%BSZ) */
|
||
long st_blocks; /* # pages in file (FB%PGC of .FBBYV) */
|
||
long st_spare4[2];
|
||
};
|
||
|
||
|
||
FORKEXEC() - New KCC-specific call
|
||
|
||
This call is intended to combine the functions of fork() and
|
||
exec() so that a user program that wants to perform the very common
|
||
procedure of first calling fork() and then having the child call exec()
|
||
can now simply use forkexec() and accomplish the same thing much faster.
|
||
|
||
The calling sequence is simply:
|
||
#include <frkxec.h>
|
||
int forkexec(fxp);
|
||
struct frkxec *fxp;
|
||
|
||
See the include file for details on the contents of the frkxec
|
||
structure and the flags that can be provided.
|
||
|
||
All of the exec*() functions call forkexec() with FX_NOFORK set.
|
||
|
||
TTY Handling:
|
||
|
||
The library supports many (though not all) of the Un*x TTY
|
||
functions. The primary means of getting information about the TTY and
|
||
setting TTY parameters is with the ioctl() call. All 4.3BSD TTY-related
|
||
ioctl functions are recognized, although not all are completely supported.
|
||
In particular, all requests to "get" data structures will always return
|
||
as much information as is available; attempting to "set" some elements of
|
||
these structures may or may not work, as described in the following comments.
|
||
|
||
IOCTL function comments:
|
||
|
||
FIONREAD - Get # bytes to read on FD. Supported.
|
||
|
||
TIOCGETP - Get sgttyb parameters, same as V6/V7 gtty(). Supported.
|
||
TIOCSETP - Set sgttyb parameters, same as V6/V7 stty(). Supported.
|
||
sg_ispeed, sg_ospeed Can read and set.
|
||
sg_erase Cannot set to anything but DEL (fails if you try).
|
||
sg_kill Cannot set to anything but ^U (fails if you try).
|
||
sg_flags The following flags are used:
|
||
RAW, CRMOD, ECHO, CBREAK
|
||
All other flags are ignored, esp. LCASE and TANDEM.
|
||
|
||
TIOCSETN - V7: same as TIOCSETP, but without flushing TTY input. Supported.
|
||
TIOCEXCL - V7: set exclusive use of tty (not implemented).
|
||
TIOCNXCL - V7: reset exclusive use of tty (not implemented).
|
||
TIOCHPCL - V7: Hang up on last close (not implemented).
|
||
TIOCFLUSH - V7: Flush TTY input and output buffers. Supported.
|
||
|
||
All other functions are for BSD4.3.
|
||
|
||
TIOCSTI - Simulate terminal input. Supported.
|
||
TIOCSBRK - Set break bit. (not implemented)
|
||
TIOCCBRK - Clear break bit. (not implemented)
|
||
TIOCSDTR - Set data terminal ready. (not implemented)
|
||
TIOCCDTR - Clear data terminal ready. (not implemented)
|
||
TIOCGPGRP - Get pgrp of tty. (not implemented)
|
||
TIOCSPGRP - Set pgrp of tty. (not implemented)
|
||
|
||
TIOCGETC - Get special characters (tchars). Supported.
|
||
TIOCSETC - Set special characters (tchars). Supported (sort of).
|
||
t_intrc and t_quitc (for SIGINT and SIGQUIT) are initially -1 but
|
||
can be set to any control character. Note that because
|
||
chars are unsigned, the initial value when converted to
|
||
an integer is 0777, not -1!
|
||
No other elements of tchars can be set to anything but what they
|
||
already are:
|
||
t_stopc = ^S, t_startc = ^Q, t_eofc = ^Z, t_brkc = -1
|
||
|
||
|
||
TIOCLBIS - Set bits in local mode word. (not implemented)
|
||
TIOCLBIC - Clear bits in local mode word. (not implemented)
|
||
TIOCLGET - Get local mode mask. (not implemented)
|
||
TIOCLSET - Set local mode mask. (not implemented)
|
||
|
||
TIOCSLTC - Set local special chars (ltchars). Supported.
|
||
TIOCGLTC - Get local special chars (ltchars). Supported (sort of).
|
||
None of these chars can be set to anything but what they already are:
|
||
t_suspc = ^C, t_dsuspc = ^C, t_rprntc = ^R, t_flushc = ^O,
|
||
t_werasc = ^W, t_lnextc = ^V
|
||
|
||
TIOCGETD - Get line discipline. Supported.
|
||
TIOCSETD - Set line discipline. Supported (sort of).
|
||
The line discipline is always NTTYDISC and cannot be set otherwise.
|
||
|
||
TIOCGWINSZ - Get window size info. Supported.
|
||
TIOCSWINSZ - Set window size info. Supported.
|
||
ws_col and ws_row correspond to the terminal's width and height.
|
||
Both can be read and set.
|
||
ws_xpixel and wx_ypixel are initially 0 but can be set and then read.
|
||
|
||
Signals:
|
||
|
||
Signals are complicated, both on Un*x and T20/10X. The KCC
|
||
implementation by default attempts to support the 4.3BSD signal
|
||
mechanism, which uses a variety of system calls. For those planning
|
||
to use signals, the file SIGNAL.DOC should be consulted.
|