Moose Mediad Design Document

Goals

No Objectserver

The moose release will not have objectserver to kick around any more. mediad/banyan is tightly coupled to the objectserver, so new mediad needs new functions and interfaces to provide the functionality that objectserver previously provided.

Non-SCSI devices

The moose's floppy is on a parallel port, not SCSI. Mediad/banyan is 100% SCSI oriented. Moose mediad must also support non-SCSI devices such as the floppy.

Icons Stay Alive

Mediad/banyan stops monitoring devices when an application such as cdplayer or SoftWindows takes exclusive use of a device, and is unable to update the desktop's icon states. Moose mediad should continue to sniff device state and update its clients even when those apps are running.

Easily Extensible

Mediad/banyan supports 3 types of filesystem on 7 types of device. It has 3 * 7 = 21 places where it recognizes a particular filesystem on a particular device. That's getting unwieldy to maintain. We expect more devices and more filesystems in the future, so new mediad should not require M *N places where filesytems are recognized. Instead, we should have an M + N design.

Also, moose mediad should let us release support for a new device, e.g. in a patch, without re-releasing all devices. That should prevent, or at least reduce, rev locking between devices.

Better Device Alias Support

Under Irix, a particular device can be referred to by several different names. E.g., /dev/scsi/sc0d4l0, /dev/rdsk/dks0d4vol, and /dev/dsk/dks0d4s7 all refer to the same disk. Mediad recognizes device name aliases in an ad hoc way, and this is a continuing maintenance problem.

Support Hybrid/Partitioned Media

Mediad/banyan assumes that every disk has exactly one file system on it. That's not true in the case of partitioned disks nor hybrid CD-ROMs. Moose mediad should be able to mount any/all partitions, and should let the user control which one(s) are mounted.

In the first release, it will not be possible to mount more than one partition at a time.

Performance

Moose mediad should use no more system resources (memory, CPU, I/O bandwidth) than mediad/banyan. Mediad/banyan's CPU and I/O consumption are negligible, so the only real concern is resident size.

Design Overview

Moose mediad's basic design metaphor is a device, filesystem, and client interface independent core with many ancillary modules. The core implements mediad's basic logic and has interfaces to all the ancillary modules. The ancillary modules are either in the form of DSOs, where we want to be able to field-extend various modules, or linked into the mediad executable, where we don't want field-extensibility.

Device/Partition Addressing

Rather than referring to devices by name (/dev/whatever) or by SCSI controller/ID/LUN, moose mediad defines a DeviceAddress as an abstract type, whose concrete subtypes are SCSIAddress and ParallelAddress. More subtypes can be defined as needed. The SCSIAddress is, sure enough, composed of <controller, ID, LUN>. Since moose has a single parallel port, a ParallelAddress is unique. DeviceAddresses may be compared for equality, using operator ==. There are routines to convert a H/W inventory record or a SCSI triple to a DeviceAddress.

A PartitionAddress on a device is defined as the tuple <DeviceAddress, format, number>, where DeviceAddress is as defined above, format is an enum of the known filesystem formats, and number is the partition number of that particular format. Partition numbers are defined per-format. E.g., DOS uses 1-4 for primary partitions and 5-8 for extended partitions. The constant PartitionAddress::WholeDisk can be used as a partition number. Note that a PartitionAddress does not specify sector numbers. In some cases, an address exists when there is no specific device to attach it to, and no specific sector numbers. Weird, but necessary.

Both DeviceAddress and PartitionAddress are values. By that I mean that instances can be created and destroyed without side effects.

Device DSOs

Each supported device has a separate DSO containing the code that knows how to talk to that specific device and also describing that device's features. Each DSO has a routine named "instantiate" which looks at a DeviceInfo record and decides whether that DSO knows that device. If so, the instantiate routine returns a pointer to new Device object. Otherwise, it returns nil.

Device objects are derived from class Device. Look at Device.H to see the exact interface of a device.

Format DSOs

Each supported filesystem format has a separate DSO containing the code that can recognize that format. Each format DSO has an "inspect" routine that is called to inspect a device for filesystems of that format. When the inspect routine finds one or more filesystems, it instantiates Partitions by calling Partition::create(), then instantiates Volumes by calling SimpleVolume::create().

Mediad Core

The mediad core scans the hardware inventory and attempts to instantiate a Device subclass on each record in the inventory. When it finds a supported device, it creates a DeviceMonitor to watch that device. The DeviceMonitor accesses the hardware through the Device subclass. The DeviceMonitor also generates events on insertion, ejection, mount, and dismount, and responds to commands to eject, lock or unlock devices.

Client Interfaces

There are three client interfaces. They are the CompatClient, the MonitorClient, and FsdClient interfaces. CompatClient is the same interface as mediad/banyan supported. MonitorClient is a new interface which describes the state of all removable devices and volumes in the system, and generates events when that state changes. FsdClient monitors changes to /etc/fsd.auto through the miracle of fam, and changes mediad's behavior when that file changes.

Invocation and Command Line Arguments

Mediad supports most of the command line arguments implemented by mediad/banyan. It also is hard-linked to the /usr/sbin/eject command, and it can also be invoked by inetd, in the case where a remote MonitorClient wants to see the system state, but mediad isn't already running.

Mediad Objects

Since mediad is object oriented, it makes sense to describe its design in terms of its objects. Each class is described in detail in its header file.

Support Classes

These classes provide nifty support functions for the rest of mediad.

Enumerable

Several classes are subclasses of Enumerable. By subclassing Enumerable, a class gets a variety of methods for enumerating its instances. To access all elements sequentially, use first(), next(), prev() and last(). To access elements nonsequentially, use index(), nth() and count().

Scheduler and friends

The Scheduler is based on select(2). A module can create a Task to be executed at some time or at regular intervals, or it can create an IOHandler, which will be called when a descriptor becomes ready for I/O. IOHandler is abstract - the three concrete subclasses are ReadHandler, WriteHandler, and ExceptHandler.

FAMonitor and subclasses

FAMonitor is an abstract class of "filesystem entity monitored by FAM". The two subclasses are FileMonitor and DirectoryMonitor. When the file or directory is changed, the FAMonitor's callback is called.

DSReq

DSReq is a wrapper around the dsreq_t; see dslib(3X). The DSReq has the property that it automatically opens and closes the /dev/scsi device - there's no need to keep track of who opened it and who closes it, and there's no way to introduce bugs by forgetting to put dsclose() into one code path.

Log

The Log class logs messages to syslog or to standard error.

Devices, Partitions, Volumes and Addresses

DeviceAddress, SCSIAddress, PartitionAddress

These are described above in the Design Overview section.

DeviceInfo

When mediad is probing for devices, it creates a DeviceInfo record and hands it to each of the device DSOs' instantiate routines. The DeviceInfo is a grab bag of potentially useful info. It includes a H/W inventory record, a DeviceAddress, and, for SCSI devices, the result of a SCSI INQUIRY command.

Device

Device is the abstract superclass from which device objects are derived. It has a bunch of methods for obtaining info about the device and for controlling the device.

Devices are enumerated. Whenever a MonitorClient receives an event, mediad sends it a list of all Device objects. So a Device object is only created when there's a real device that mediad is really monitoring.

Each device has three methods for returning its name. The methods are short_name(), ftr_name() and dev_name(). This is ugly, but necessary.

The short name is the common name of the device. It corresponds to objectserver's resourceObject name attribute. Prepend a "/" to it to form the device's default mount point. The short name has no punctuation characters. (e.g., CDROM, floppy2)

The ftr name is the name as it appears in the file type rules. Devices don't necessarily have unique ftr names. For example, three CD-ROM drives would all have cdrom as their ftr name.

The dev name is the name of the /dev entry that should be used to open that device. The dev_name method takes a filesystem format and a partition number as an argument. So, for example, CDROM::fmt_name(FMT_CDDA, WholeDisk) returns /dev/scsi/sc<whatever>, while CDROM::fmt_name(FMT_EFS, 7) returns /dev/dsk/dks<whatever>s7.

Partition

A Partition is a piece of a disk. A partition has a format associated with it, either one of the filesystem formats or "raw". It also has sector info: sector size, starting sector, and number of sectors. The last two are 64 bit integers.

Every device with media has one partition in raw format that describes the total capacity of the media.

Partitions are enumerated. Whenever a MonitorClient receives an event, mediad sends it a list of all Partition objects. So a Partition object is only created when there's a real partition on a disk.

Volume, SimpleVolume

A Volume is a lot like a filesystem. Volume is an abstract base class. A volume has one or more Partitions, a label, and info about how to mount it, in the form of a struct mntent.

SimpleVolume is the concrete subclass of Volume. It's a volume with one partition. Mediad does not currently support multi-partition volumes, but the structure is in place to add that support by defining a new subclass of Volume.

Volumes are enumerated. Whenever a MonitorClient receives an event, mediad sends it a list of all Volume objects. So a Volume object is only created when there's a real volume on disk(s). The volume doesn't have to be mounted.

Core Objects

MediaDaemon

MediaDaemon is the object that probes for devices in the system and creates DeviceMonitors to monitor them. There is exactly one MediaDaemon object.

DeviceMonitor

DeviceMonitor is the object that monitors one device. It wakes up periodically and checks the device's state. When a medium is inserted, the DeviceMonitor calls each of the FormatDSOs to inspect the medium. The FormatDSOs may create Volumes and Partitions. When a medium is ejected, the DeviceMonitor destroys any Volumes or Partitions associated with that device.

A DeviceMonitor responds to several commands, activate(), deactivate(), suspend(), resume(), and eject(). Eject is obvious. Activate/deactivate occur in response to libmediad's release/get exclusive use functions. Suspend/resume occur when mon=on/off is set in /etc/fsd.auto.

DeviceLibrary, FormatLibrary

These are silly classes that maintain a list of all the device DSOs and format DSOs. Both DSO libraries look in /usr/lib/devicelib, and find all files matching a pattern, either dev_*.so or fmt_*.so, respectively.

DSO, DeviceDSO, FormatDSO

DSO is the abstract base class for a Dynamic Shared Object. It has methods to load/unload, and to look up a symbol. These are just thin wrappers around dlopen, dlclose and dlsym. The concrete subclasses are DeviceDSO and FormatDSO.

DeviceDSO has a method, instantiate(), that calls the DSO's instantiation routine. DeviceDSO maintains a reference count of the number of instances each DSO has so that a DSO won't be unloaded while instances exist.

FormatDSO has a method, inspect(), that calls the DSO's inspection routine.

Client Interfaces

CompatListener, CompatClient

The CompatListener listens for client connections from clients using the mediad/banyan interface. It listens on a Unix domain socket. When it gets a connection, it creates a CompatClient object. There is only one CompatListener object.

The CompatClient object handles a single session with an old-style client. Each session consists of a single request-response pair. These are the requests.

RPCListener, MonitorClient

The RPCListener listens for RPC connections from monitor clients. When it gets one, it creates a MonitorClient object. There is only one RPCListener object. The protocol used is TCP/IP, so mediad will accept connections from remote clients. (But only if the share_devices chkconfig option is on.)

A MonitorClient object handles a single session with a monitor client. When it first connects, and when certain events happen, it sends a message to the client. These are the events.

Each MonitorClient registers itself to receive callbacks from the mediad core. The callbacks occur when a DeviceMonitor detects that a medium has been inserted, ejected, or reformatted, and when mediad mounts or dismounts a filesystem.

The message sent to the client is XDR-encoded. It has two main parts: an event record and the system state. The event record has an event code and either a device number (for an MCE_INSERTION, MCE_EJECTION, or MCE_REFORMAT event) or a volume number (for MCE_MOUNT or MCE_DISMOUNT). The system state is a list of all devices, all partitions, and all volumes known to mediad, along with a variety of attributes about each.

FSDMonitor

The FSDMonitor is not yet implemented. It will monitor /etc/fsd.auto for changes using fam, and it will update mediad's state appropriately. I'm not sure how it will work yet.

DSOs

Device DSOs

This section to be written. Ought to describe what the DSOs do in detail, as they are above.

Format DSOs

Ditto.

Compact Discs

This section to be written. CDs are ugly, and our CD support is ugly too.

Invocation

Mediad can be invoked several different ways. The executable figures out how it was invoked and does the right thing.

Normally, mediad is started at boot time from /etc/init.d/mediad. In this mode, it scans the system for monitorable devices and, if it finds any, sits and monitors them.

Mediad may also be invoked by inetd(1M), in response to a monitor client's attempt to connect. In this case, mediad tries to forward the connection to the real mediad, and if it can't, it replies that there are no removable devices.

Mediad is also linked to the eject command. When invoked as eject, mediad tries to send an eject message to the real mediad, using the CompatClient interface. If there is no other mediad running, mediad probes the specific device requested, finds its DeviceDSO, and tells the DeviceDSO to eject the medium. This has the advantage that the same code is executed whether or not mediad is running, and eject is automatically extended to new devices when mediad is.

Mediad can be invoked from the command line with a variety of options. Some of the more common ones are:

Exclusive Use

This section is to be written. Briefly, the exclusive use problem has been solved, by changing all the clients that use it. Icons should be live all the time. (It's not tested yet.)