This document explains mediad's new monitor client interface. This interface is used to monitor the state of removable media devices. Both the API that clients use and the formats and meanings of data structures passed to clients are described.
The API allows a client to connect to mediad on any host, and to get a list of removable media devices and the partitions and filesystems on those devices. It also sends clients notification whenever the removable media state changes. It does not allow the client to change the removable media state in any way.
This interface is needed to replace objectserver. Prior to the moosehead release, mediad sent all its state changes to objectserver, and other clients such as the Indigo Magic Desktop or the Cadmin system administration tools got that state from objectserver. There is no objectserver in the moosehead release, so a different mechanism was needed.
A client may use this interface by linking with libmediaclient.so. libmediaclient is written in C, and it may be called from C or C++. (Probably Fortran too, if you wanted to.)
The removable media state is encoded into four data structures, mc_system, mc_device, mc_volume, and mc_partition. Logically, a system contains a set of devices, volumes and partitions. A device contains several partitions, and a volume resides on several partitions. Figure one illustrates these relationships.
Figure 1

Volumes, devices and partitions have various attributes. Those are listed here. The only ones described in detail are the device name fields.
struct mc_volume {
char *mcv_label; /* volume label, if any */
char *mcv_fsname; /* e.g., /dev/dsk/dks... */
char *mcv_dir; /* mount point */
char *mcv_type; /* volume type (format), MC_FMT_xxx */
char *mcv_subformat; /* volume subformat, MC_SBFMT_xxx */
char *mcv_mntopts; /* mount options */
unsigned int mcv_nparts; /* number of partition ptrs */
mc_partition_t **mcv_parts; /* points to array of part. ptrs */
};
struct mc_device {
char *mcd_name; /* device's short name */
char *mcd_fullname; /* device's printable name */
char *mcd_ftrname; /* device's name as used in FTRs */
inventory_t mcd_invent; /* device's H/W inventory record */
mc_bool_t mcd_monitored; /* is mediad monitoring it? */
mc_bool_t mcd_media_present; /* is media present? */
mc_bool_t mcd_write_protected; /* is device write-locked? */
unsigned int mcd_nparts; /* number of partition ptrs */
mc_partition_t **mcd_parts; /* points to array of part. ptrs */
};
struct mc_partition {
mc_device_t *mcp_device; /* device of which this is part */
char *mcp_format; /* partition format, MC_FMT_xxx */
int mcp_index; /* partition # or MC_IX_WHOLE */
unsigned int mcp_sectorsize; /* size of sector in bytes */
__uint64_t mcp_sector0; /* starting sector number */
__uint64_t mcp_nsectors; /* number of sectors in part. */
};
struct mc_system {
unsigned int mcs_n_volumes; /* number of volumes in system */
unsigned int mcs_n_devices; /* number of devices in system */
unsigned int mcs_n_parts; /* number of partitions in system */
mc_volume_t *mcs_volumes; /* ptr to array of volumes */
mc_device_t *mcs_devices; /* ptr to array of devices */
mc_partition_t *mcs_partitions; /* ptr to array of partitions */
};
A device has an array of pointers to partitions. A volume also has an array of partition pointers. A partition points to its containung device. An mc_system is nothing but three arrays: an array of volumes, an array of devices, and an array of partitions.
A device has three different names. mcd_name is a common, short name for the device, with no punctuation characters. mcd_fullname is the device's name in a more human readable format. But it is not internationalized. mcd_ftrname is the device's name as it appears in the file type rules, /usr/lib/filetype/devices/devices.ftr.
Note that no /dev entry is associated with a device. Instead, the /dev entry is associated with a volume. That's because different volumes on the same device may be mounted using different mount points. E.g., a CD-ROM would mount these filesystem types using these /dev entries.
HFS /dev/rdsk/dks0d4vol EFS /dev/rdsk/dks0d4s7 CDDA /dev/scsi/sc0d4l0
Every device that has media present has one mcd_partition of format "raw". That partition is not part of any volume; it describes the raw size of the medium.
typedef void (*mc_event_proc_t)(mc_port_t, mc_closure_t, const mc_event_t *);
The client must define an event procedure. This procedure will be called when mediad is first connected to and whenever mediad has an event for the client.
mc_port_t mc_create_port(u_long IPaddress, mc_event_proc_t, mc_closure_t); void mc_destroy_port(mc_port_t);
To use the monitor interface, a client creates an mc_port. The mc_port makes a connection to mediad, retrying until it's successful. When it gets connected, it calls the client's event procedure. A port is created using mc_create_port() and destroyed using mc_destroy_port().
struct mc_what_next {
enum { MC_IDLE, MC_INPUT, MC_OUTPUT, MC_TIMEOUT } mcwn_what;
/* N.B. MC_IDLE never seen */
int mcwn_fd; /* descriptor for select */
unsigned mcwn_seconds; /* timeout duration */
};
const mc_what_next_t *mc_execute(mc_port_t);
The client has to give the port control at various times. To do so, the
client calls mc_execute(). mc_execute() returns a mc_what_next structure to inform the client when it should be called next. If the mcwn_what field is MC_INPUT, the client should call mc_execute() when the descriptor in the mcwn_fd field is readable, according to select(2). If the mcwn_what field is MC_OUTPUT, the client should call mc_execute() when the descriptor is writable. If mcwn_what is MC_TIMEOUT, the client should sleep for mcwn_seconds seconds, then call mc_execute().
The client's event procedure is called from mc_execute().
int mc_port_connect(mc_port_t);
If your client doesn't need to do other processing asynchronously while
waiting to connect to mediad, it may use mc_port_connect() instead of repeatedly calling mc_execute() and select(). A client that uses mc_port_connect() would look like this.
{
mc_port_t port;
/* ... */
port = mc_create_port(...);
int fd = mc_port_connect(port);
/* ... application main loop ... */
/* ... select on fd for reading as long as
mc_execute() returns MC_INPUT ... */
mc_destroy_port(port);
/* ... */
}
const mc_system_t *mc_system_state(mc_port_t);
mc_system_state() returns a pointer to an mc_system structure describing mediad's current state. If the client is not connected
to mediad, mc_system_state() returns NULL. The mc_system structure returned belongs to the port: the
client should not destroy it, and should not expect it to be preserved after
control flow returns to libmediaclient.
mc_system_t *mc_dup_system(const mc_system_t *);
mc_dup_system() may be used to make a copy of a system's state. The new mc_system and related data structures belong to the caller, and should be freed by the caller.
void mc_destroy_system(mc_system_t *);
mc_destroy_system() is used to destroy an mc_system created by mc_dup_system().
There is a sample client available. Its source is available at /proj/???/isms/irix/cmd/mediad/clientmain.c.