# 20 Operating System Support: `OS`

This chapter describes the procedures contained in the module `OS`

## 20.1 Conventions

Most procedures can be seen as straightforward lifting of POSIX compatible operating system functions to Oz. Hence, our description consists mainly of a reference to the relevant Unix manual page. If you are running on a Windows based platform you should consult a POSIX documentation (e. g. [Lew91]).

A major convention is that most `int` arguments in C are integers in Oz. Moreover `char*` arguments are virtual strings, if they are used as input in C. If they are used as output in C they are strings. An n-ary C-function returning a value is implemented as a n+1-ary Oz procedure where the last argument serves as output position.

Whenever one needs predefined POSIX constants, they can be used as Oz atoms. Wherever bitwise disjunction of predefined constants is used in C, a list of atoms (the constant names) is allowed, which is interpreted as bitwise disjunction of their respective values.

The functionality of the module `OS` can be classified as procedures which are useful in applications, or procedures needed for building high level functionality. The latter are only interesting for programmers who want to build abstractions similar to those provided by the `Open` module.

## 20.2 Exceptions

If the operating system returns an error, an Oz Exception is raised which looks as follows:

> `system(os(``A``` `S1``` `I``` `S2``) debug:``X``)`

where:

1. The atom `A` gives the category of the error (e. g. `os` for operating system or `net` for network layer).

2. The string `S1` describes the operating system routine that raised the exception.

3. The integer `I` gives an operating system dependent error number.

4. The string `S2` describes the exception by some text.

5. The value `X` might contain some additional debug information.

## 20.3 Random Integers

`rand`

  > `{OS.rand` `?I``}`

  Returns a randomly generated integer.

  See `rand(3)`.

`srand`

  > `{OS.srand` `+I``}`

  Sets the seed for the random number generator used by `OS.rand`. If `I` is `0`, the seed will be generated from the current time.

  See `srand(3)`.

`randLimits`

  > `{OS.randLimits` `?MinI``` `?MaxI``}`

  Binds `MinI` and `MaxI` to the smallest and greatest possible random number obtainable by `OS.rand`.

  See `rand(3)`.

## 20.4 Files

`tmpnam`

  > `{OS.tmpnam` `?FileNameS``}`

  Returns a freshly created full pathname.

  See `tmpnam(2)`.

`unlink`

  > `{OS.unlink` `+PathV``}`

  Removes the file with name `PathV`.

  See `link(2)`.

## 20.5 Directories

`getDir`

  > `{OS.getDir` `+PathV``` `?FileNameSs``}`

  Returns a list of strings giving the files in the directory `PathV`.

  See `opendir(3)` and `readdir(3)`.

`getCWD`

  > `{OS.getCWD` `?FileNameS``}`

  Returns in `FileNameS` the path of the current working directory.

  See `getcwd(3)`.

`stat`

  > `{OS.stat` `+PathV``` `?StatR``}`

  Returns a record describing the status of the file `PathV`.

  `StatR` has features `size`, `type`, and `mtime`. The subtree at the feature `size` is an integer that gives the size of the file `PathV`. The subtree at the feature `type` is one of the following atoms: `reg` (regular file), `dir` (directory), `chr` (character special file), `blk` (block special file), `fifo` (pipe or FIFO special file), `unknown` (something else). `mtime` is the time of last modification measured in seconds since the dawn of the world, which, as we all know, was January 1, 1970.

  See `stat(2)`.

`chDir`

  > `{OS.chDir` `+PathV``}`

  Changes the current working directory to `PathV`.

  See `chdir(2)`.

`mkDir`

  > `{OS.mkDir` `+PathV``` `+ModeAs``}`

  Creates a directory `PathV`. Access modes are specified by `ModeAs`, similar to `OS.open`.

  See `mkdir(2)`.

## 20.6 Sockets

`getServByName`

  > `{OS.getServByName` `+NameV``` `+ProtoV``` `?PortIB``}`

  Returns the port number `PortI` of a service `NameV` reachable in the Internet domain with the specified protocol `ProtoV`.

  If the service is unknown, `false` is returned.

  As an example, the application

  > `{OS.getServByName "finger" "tcp" Port}`

  binds `Port` to the number, where you can connect to your local finger server.

  See `getservbyname(3)`.

`getHostByName`

  > `{OS.getHostByName` `+NameV``` `?HostentR``}`

  Returns name information for the host `NameV`.

  The record HostentR has the following features:

  `name` | official host-name | string
  `aliases` | alternative host-names | list of strings
  `addrList` | Internet addresses | list of strings

  See `gethostbyname(3)`.

## 20.7 Time

`time`

  > `{OS.time` `?TimeI``}`

  Returns the time since 00:00:00 GMT, Jan. 1, 1970 in seconds.

  See `time(2)`.

`gmTime`

  > `{OS.gmTime` `?GmTimeR``}`

`localTime`

  > `{OS.localTime` `?LocalTimeR``}`

  Returns a description of the Coordinated Universal Time (UTC), respectively a description of the local time.

  The records `GmTimeR` and `LocalTimeR` have the following features, where the fields are all integers:

  `sec` | seconds | 0 - 61
  `min` | minutes | 0 - 59
  `hour` | hours | 0 - 23
  `mDay` | day of month | 1 - 31
  `mon` | month of year | 0 - 11
  `year` | years since 1900 |
  `wDay` | days since Sunday | 0 - 6
  `yDay` | day of year | 0 - 365
  `isDst` | 1 if daylight savings time in effect (DST) |

  See `gmtime(3)`, `localtime(3)`, and `time(2)`.

## 20.8 Environment Variables

`getEnv`

  > `{OS.getEnv` `+NameV``` `?ValueSB``}`

  Returns the value of the environment variable `NameV`.

  If a variable with the given name does not exist, the procedure returns `false`.

  As an example, consider:

  > `{OS.getEnv 'OZHOME'}`

  returns where the Oz system has been installed. This information is also available via `{System.get home}`

  See `getenv(3)`.

`putEnv`

  > `{OS.putEnv` `+NameV``` `+ValueV``}`

  Sets the value of the environment variable `NameV` to `ValueV`.

  See `putenv(3)`.

## 20.9 Miscellaneous

`uName`

  > `{OS.uName` `?UtsnameR``}`

  Returns system information.

  The record `UtsnameR` has at least the following features, where all fields are strings:

  `sysname` | operating system name
  `nodename` | computer name
  `release` | operating system release
  `version` | operating system version
  `machine` | machine architecture

  See `uname(2)`.

`system`

  > `{OS.system` `+CmdV``` `?StatusI``}`

  Starts a new operating system shell in which the OS command `CmdV` is executed. The status of the command is reported by `StatusI`.

  See `system(3)`.

## 20.10 Low Level Procedures

### 20.10.1 Basic Input and Output

`open`

  > `{OS.open` `+FileNameV``` `+FlagsAs``` `+ModeAs``` `?DescI``}`

  Opens a file for reading and/or writing.

  `FlagsAs` must be a list with some of the following atoms as elements:

  `'O_RDONLY'` | `'O_WRONLY'` | `'O_RDWR'`
  `'O_APPEND'` | `'O_CREAT'` | `'O_EXCL'`
  `'O_TRUNC'` | `'O_NOCCTY'` | `'O_NONBLOCK'`
  `'O_SYNC'` |  |

  Their meanings coincide with their usual POSIX meanings.

  In the same manner `ModeAs` must be a list with elements drawn from:

  `'S_IRUSR'` | `'S_IWUSR'` | `'S_IXUSR'`
  `'S_IRGRP'` | `'S_IWGRP'` | `'S_IXGRP'`
  `'S_IROTH'` | `'S_IWOTH'` | `'S_IXOTH'`

  See `open(2)` and `chmod(2)`.

`fileDesc`

  > `{OS.fileDesc` `+FileDescA``` `?FileDescIB``}`

  Maps the atoms

  `'STDIN_FILENO'`
  `'STDOUT_FILENO'`
  `'STDERR_FILENO'`

  to integers, giving their respective file descriptor. Note that these descriptors will be duplicated.

  See `open(2)` and `dup(2)`.

`read`

  > `{OS.read` `+DescI``` `+MaxI``` `?ListS``` `TailX``` `?ReadI``}`

  Reads data from a file or socket. Yields a list of characters in `ListS`, where the tail of the list is constrained to `TailX`.

  Implements the `read(2)` system call.

`write`

  > `{OS.write` `+DescI``` `+V``` `?StatusTI``}`

  Writes the virtual string `V` to a file or a socket by using the function `write(2)`.

  Illegal parts of the virtual string `V` are simply ignored, and the legal parts are written.

  If `V` contains an undetermined variable, `StatusTI` is bound to a ternary tuple with label `suspend`. The first argument is an integer describing the portion already written, the second the undetermined variable, and the last to the not yet written part of `V`.

  Otherwise `StatusTI` is bound to the number of characters written.

`lSeek`

  > `{OS.lSeek` `+DescI``` `+WhenceA``` `+OffsetI``` `?WhereI``}`

  Positions the seek pointer of a file. This procedure is implemented by the `lseek(2)` function. `WhenceA` must be one of the atoms `'SEEK_SET'`, `'SEEK_CUR'`, and `'SEEK_END'`.

`close`

  > `{OS.close` `+DescI``}`

  Closes a file or socket by using the `close(2)` function.

### 20.10.2 From Blocking to Suspension

If reading from a file or a socket via an operating system function, it is possible that no information is available for reading. Furthermore, an attempt to write something to a file or to a socket might not be possible at a certain point in time. In this case the OS function blocks, i. e. the whole Oz Emulator process will stop doing any work.

To overcome this problem, we provide three procedures. These procedures will suspend rather than block.

`acceptSelect`

  > `{OS.acceptSelect` `+DescI``}`

`readSelect`

  > `{OS.readSelect` `+DescI``}`

`writeSelect`

  > `{OS.writeSelect` `+DescI``}`

  Blocks until a socket connection can be accepted (data is present to be read, and writing of data is possible, respectively) at the file or socket with descriptor `DescI`

  For example the following case statement (we assume that `Desc` is bound to a descriptor of a socket):

  > `{OS.readSelect Desc}
  > case {OS.read Desc 1024 ?S nil}
  > of` ...`then`...`
  > end`

  blocks the current thread until data for reading is available.

  This functionality is implemented by the `select(2)` system call.

  Before actually closing a file descriptor, the following procedure needs to be called.

`deSelect`

  > `{OS.deSelect` `+DescI``}`

  Discards all threads depending on the file descriptor `DescI`.

### 20.10.3 Sockets

`socket`

  > `{OS.socket` `+DomainA``` `+TypeA``` `+ProtoV``` `?DescI``}`

  Creates a socket. Is implemented by the function `socket(2)`.

  `DomainA` must be either the atom `'PF_INET'` or `'PF_UNIX'`, whereas `TypeA` must be either `'SOCK_STREAM'`, or `'SOCK_DGRAM'`.

  `ProtoV` must be a virtual string. If it denotes the empty string, an appropriate protocol is chosen automatically, otherwise it must denote a valid protocol name like `"tcp"` or `"udp"`.

`bind`

  > `{OS.bind` `+SockI``` `+PortI``}`

  Binds a socket to its global name.

  See `bind(2)`.

`listen`

  > `{OS.listen` `+SockI``` `+BackLogI``}`

  Indicates that a socket is willing to receive connections.

  See `listen(2)`.

`accept`

  > `{OS.accept` `+SockI``` `?HostS``` `?PortI``` `?DescI``}`

  Accepts a connect request on a socket.

  `HostS` is bound to a string describing the host name. It is possible to use `OS.acceptSelect` to block a thread until a connect attempt on this socket is made.

  See `accept(2)` and `gethostbyaddr(3)`.

`connect`

  > `{OS.connect` `+SockI``` `+HostV``` `+PortI``}`

  Connects to a socket.

  See `connect(2)` and `gethostbyaddr(3)`.

`shutDown`

  > `{OS.shutDown` `+SockI``` `+HowI``}`

  Signals that a socket is not longer interested in sending or receiving data.

  See `shutdown(2)`.

`getSockName`

  > `{OS.getSockName` `+SockI``` `?PortI``}`

  Gets the name of a socket.

  See `getsockname(3)`.

`send`

  > `{OS.send` `+SockI``` `+MsgV``` `+FlagsAs``` `?LenI``}`

`sendTo`

  > `{OS.sendTo` `+SockI``` `+MsgV``` `+FlagsAs``` `+HostV``` `+PortI``}` `?LenI``}`

  Sends data from a socket.

  `FlagsAs` must be a list of atoms; its elements must be either `'MSG_OOB'` or `'MSG_DONTROUTE'`.

  See `send(2)` and `gethostbyname(3)`.

`receiveFrom`

  > > `{OS.receiveFrom` `+SockI``` `+MaxI``` `+FlagsAs``` `?MsgS``
  > > ` `+TailX``` `?HostS``` `?PortI``` `?LenI``}`

  Receives data at a socket.

  `FlagsAs` must be a list of atoms; its elements must be either `'MSG_OOB'` or `'MSG_PEEK'`.

  See `recvfrom(2)` and `gethostbyaddr(3)`.

### 20.10.4 Process Control

`kill`

  > `{OS.kill` `+PidI``` `+SigA``` `?StatusI``}`

  Implements the `kill(2)` function. `PidI` is the process identifier, `SigA` is an atom describing the signal to be sent: depending on the platform different signals are supported; at least the following signals are supported on all platforms: `'SIGTERM'`, `'SIGINT'`.

`pipe`

  > `{OS.pipe` `+CmdV``` `+ArgsVs``` `?PidI``` `?StatusT``}`

  Forks a OS process which executes the command `CmdV` with arguments `ArgsVs`.

  `PidI` is bound to the process identifier. `StatusT` is bound to a pair of socket or file descriptors where the standard input, the standard output and the standard error is redirected to. The first field of the pair is the descriptor for reading whereas the second field is the descriptor for writing.

  See `socketpair(2)`, `fork(2)`, `execvp(2)`, and `execve(2)`.

`wait`

  > `{OS.wait` `?PidI``` `?StatI``}`

  Implements the `wait(2)` function.

`getPID`

  > `{OS.getPID` `?PidI``}`

  Returns the current process identifier as integer. See `getpid(2)`.