614 lines
14 KiB
C
614 lines
14 KiB
C
#include "MonitorClient.H"
|
|
|
|
#include <assert.h>
|
|
#include <bstring.h>
|
|
#include <mediaclient.h>
|
|
#include <mntent.h>
|
|
#include <unistd.h>
|
|
#include <xdr_mc.h>
|
|
|
|
#include "CallBack.H"
|
|
#include "Device.H"
|
|
#include "DeviceMonitor.H"
|
|
#include "Event.H"
|
|
#include "Log.H"
|
|
#include "Partition.H"
|
|
#include "Scheduler.H"
|
|
#include "Volume.H"
|
|
|
|
MonitorClient::MonitorClient(int fd, bool is_remote)
|
|
: _handler(read_handler, this),
|
|
_wants_forced_unmount(false),
|
|
_is_remote(is_remote)
|
|
{
|
|
// Wrap an xdrrec stream around the fd.
|
|
|
|
xdrrec_create(&_xdrs, 0, 0, this, read_proc, write_proc);
|
|
|
|
// Activate read handler.
|
|
|
|
_handler.fd(fd);
|
|
_handler.activate();
|
|
|
|
CallBack::add(event_proc, this);
|
|
|
|
// Dump the world's state now.
|
|
|
|
mc_event event = { MCE_NO_EVENT, -1, -1 };
|
|
transmit_world(&event);
|
|
}
|
|
|
|
MonitorClient::~MonitorClient()
|
|
{
|
|
CallBack::remove(event_proc, this);
|
|
|
|
xdr_destroy(&_xdrs);
|
|
(void) close(_handler.fd());
|
|
}
|
|
|
|
void
|
|
MonitorClient::read_handler(int fd, void *closure)
|
|
{
|
|
MonitorClient *client = (MonitorClient *) closure;
|
|
|
|
// If the data read consists of a single 'S'; then that's the
|
|
// indication that the client wants to receive forced unmount events.
|
|
|
|
char byte = ' ';
|
|
ssize_t count = read(fd, &byte, 1);
|
|
|
|
if (count == 1 && byte == 'S' && !client->is_remote())
|
|
client->_wants_forced_unmount = true;
|
|
|
|
// close down on EOF.
|
|
if (count == 0)
|
|
delete client;
|
|
}
|
|
|
|
void
|
|
MonitorClient::event_proc(const Event& event, void *closure)
|
|
{
|
|
MonitorClient *client = (MonitorClient *) closure;
|
|
|
|
mc_event mce;
|
|
switch (event.type())
|
|
{
|
|
case Event::Config:
|
|
mce.mce_type = MCE_CONFIG;
|
|
break;
|
|
|
|
case Event::Insertion:
|
|
mce.mce_type = MCE_INSERTION;
|
|
break;
|
|
|
|
case Event::Ejection:
|
|
mce.mce_type = MCE_EJECTION;
|
|
break;
|
|
|
|
case Event::Suspend:
|
|
mce.mce_type = MCE_SUSPEND;
|
|
break;
|
|
|
|
case Event::Resume:
|
|
mce.mce_type = MCE_RESUME;
|
|
break;
|
|
|
|
case Event::Mount:
|
|
mce.mce_type = MCE_MOUNT;
|
|
break;
|
|
|
|
case Event::Dismount:
|
|
mce.mce_type = MCE_DISMOUNT;
|
|
break;
|
|
|
|
case Event::ForceUnmount:
|
|
mce.mce_type = MCE_FORCEUNMOUNT;
|
|
break;
|
|
|
|
case Event::Export:
|
|
mce.mce_type = MCE_EXPORT;
|
|
break;
|
|
|
|
case Event::Unexport:
|
|
mce.mce_type = MCE_UNEXPORT;
|
|
break;
|
|
|
|
default:
|
|
Log::error("MonitorClient::event_proc unknown event %d",
|
|
(int) event.type());
|
|
return;
|
|
}
|
|
Device *dev = event.device();
|
|
if (dev)
|
|
{ if (client->is_remote() && !dev->is_shared())
|
|
return; // Drop events for unshared devices.
|
|
mce.mce_device = client->count_devices(dev);
|
|
}
|
|
else
|
|
mce.mce_device = -1;
|
|
Volume *vol = event.volume();
|
|
if (vol)
|
|
{ if (client->is_remote() && !vol->devices_are_shared())
|
|
return; // Drop events for unshared volumes.
|
|
mce.mce_volume = client->count_volumes(vol);
|
|
}
|
|
else
|
|
mce.mce_volume = -1;
|
|
if (mce.mce_type == MCE_FORCEUNMOUNT)
|
|
{
|
|
if (client->wants_forced_unmount())
|
|
{
|
|
assert(!client->is_remote());
|
|
client->transmit_world(&mce);
|
|
char response;
|
|
(void) read_proc(client, &response, 1);
|
|
return;
|
|
}
|
|
// forced unmount is a nop for all clients who haven't requested it
|
|
return;
|
|
}
|
|
client->transmit_world(&mce);
|
|
}
|
|
|
|
int
|
|
MonitorClient::read_proc(void *closure, void *vbuffer, u_int nbytes)
|
|
{
|
|
MonitorClient *client = (MonitorClient *) closure;
|
|
|
|
int fd = client->_handler.fd();
|
|
char *buffer = (char *) vbuffer;
|
|
|
|
return read(fd, buffer, nbytes);
|
|
}
|
|
|
|
int
|
|
MonitorClient::write_proc(void *closure, void *vbuffer, u_int nbytes)
|
|
{
|
|
MonitorClient *client = (MonitorClient *) closure;
|
|
|
|
int fd = client->_handler.fd();
|
|
char *buffer = (char *) vbuffer;
|
|
|
|
return write(fd, buffer, nbytes);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Enumeration. We do not use the nifty enumeration methods defined
|
|
// in Enumerable. Instead, we have our own, and we get a different
|
|
// set of volumes/partitions/devices depending on whether the client
|
|
// is local or remote. Remote clients can't see any devices that
|
|
// are not shared, nor partitions on those devices, nor volumes that
|
|
// contain unshared partitions. Local clients can see everything.
|
|
|
|
// count_partitions(NULL) returns the number of (shared?) partitions
|
|
// in the system. count_partitions(p) returns the (shared?) index of
|
|
// partition p.
|
|
|
|
unsigned int
|
|
MonitorClient::count_partitions(const Partition *part)
|
|
{
|
|
if (is_remote())
|
|
{
|
|
assert(part == NULL || part->device()->is_shared());
|
|
unsigned int count = 0;
|
|
for (const Partition *p = Partition::first(); p != part; p = p->next())
|
|
{ assert(p != NULL);
|
|
if (p->device()->is_shared())
|
|
count++;
|
|
}
|
|
return count;
|
|
}
|
|
else
|
|
{
|
|
if (part)
|
|
return part->index();
|
|
else
|
|
return Partition::count();
|
|
}
|
|
}
|
|
|
|
unsigned int
|
|
MonitorClient::count_devices(const Device *dev)
|
|
{
|
|
if (is_remote())
|
|
{
|
|
assert(dev == NULL || dev->is_shared());
|
|
unsigned int count = 0;
|
|
for (const Device *dp = Device::first(); dp != dev; dp = dp->next())
|
|
{ assert(dp != NULL);
|
|
if (dp->is_shared())
|
|
count++;
|
|
}
|
|
return count;
|
|
}
|
|
else
|
|
{
|
|
if (dev)
|
|
return dev->index();
|
|
else
|
|
return Device::count();
|
|
}
|
|
}
|
|
|
|
unsigned int
|
|
MonitorClient::count_volumes(const Volume *vol)
|
|
{
|
|
if (is_remote())
|
|
{
|
|
assert(vol == NULL || vol->devices_are_shared());
|
|
unsigned int count = 0;
|
|
for (const Volume *vp = Volume::first(); vp != vol; vp = vp->next())
|
|
{ assert(vp != NULL);
|
|
if (vp->devices_are_shared())
|
|
count++;
|
|
}
|
|
return count;
|
|
}
|
|
else
|
|
{
|
|
if (vol)
|
|
return vol->index();
|
|
else
|
|
return Volume::count();
|
|
}
|
|
}
|
|
|
|
Partition *
|
|
MonitorClient::nth_partition(unsigned n)
|
|
{
|
|
if (is_remote())
|
|
{
|
|
unsigned int count = 0;
|
|
for (Partition *pp = Partition::first(); ; pp = pp->next())
|
|
{ assert(pp != NULL);
|
|
if (pp->device()->is_shared())
|
|
if (count++ == n)
|
|
return pp;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return Partition::nth(n);
|
|
}
|
|
}
|
|
|
|
Device *
|
|
MonitorClient::nth_device(unsigned n)
|
|
{
|
|
if (is_remote())
|
|
{
|
|
unsigned int count = 0;
|
|
for (Device *dp = Device::first(); ; dp = dp->next())
|
|
{ assert(dp != NULL);
|
|
if (dp->is_shared())
|
|
if (count++ == n)
|
|
return dp;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return Device::nth(n);
|
|
}
|
|
}
|
|
|
|
Volume *
|
|
MonitorClient::nth_volume(unsigned n)
|
|
{
|
|
if (is_remote())
|
|
{
|
|
unsigned int count = 0;
|
|
for (Volume *vp = Volume::first(); ; vp = vp->next())
|
|
{ assert(vp != NULL);
|
|
if (vp->devices_are_shared())
|
|
if (count++ == n)
|
|
return vp;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return Volume::nth(n);
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// XDR. This stuff is very ugly.
|
|
|
|
struct my_xdr_mc_closure {
|
|
xdr_mc_closure mc_stuff;
|
|
MonitorClient *client;
|
|
Volume *vol;
|
|
Device *dev;
|
|
Partition *part;
|
|
};
|
|
|
|
void
|
|
MonitorClient::transmit_world(const mc_event *event)
|
|
{
|
|
my_xdr_mc_closure mxmc = {
|
|
{
|
|
xdr_volpartptrs,
|
|
xdr_devpartptrs,
|
|
xdr_devptr,
|
|
},
|
|
this,
|
|
NULL, NULL, NULL,
|
|
};
|
|
|
|
_xdrs.x_op = XDR_ENCODE;
|
|
_xdrs.x_public = (caddr_t) &mxmc;
|
|
(void) xdr_world(&_xdrs, (mc_event *) event);
|
|
xdrrec_endofrecord(&_xdrs, TRUE); // Flush world out.
|
|
}
|
|
|
|
#if 0
|
|
void
|
|
MonitorClient::transmit_zap(const mc_event *event)
|
|
{
|
|
unsigned int n_things = 0;
|
|
|
|
_xdrs.x_op = XDR_ENCODE;
|
|
(xdr_u_int(&_xdrs, &n_things) &&
|
|
xdr_u_int(&_xdrs, &n_things) &&
|
|
xdr_u_int(&_xdrs, &n_things) &&
|
|
xdr_mc_event(&_xdrs, (mc_event *) event) &&
|
|
xdr_enumerate(&_xdrs, &n_things, NULL) &&
|
|
xdr_enumerate(&_xdrs, &n_things, NULL) &&
|
|
xdr_enumerate(&_xdrs, &n_things, NULL));
|
|
xdrrec_endofrecord(&_xdrs, TRUE); // Flush zap out.
|
|
}
|
|
#endif
|
|
|
|
bool_t
|
|
MonitorClient::xdr_world(XDR *xdrs, mc_event *event)
|
|
{
|
|
unsigned int n_partitions = count_partitions();
|
|
unsigned int n_devices = count_devices();
|
|
unsigned int n_volumes = count_volumes();
|
|
|
|
return (xdr_u_int(xdrs, &n_volumes) &&
|
|
xdr_u_int(xdrs, &n_devices) &&
|
|
xdr_u_int(xdrs, &n_partitions) &&
|
|
xdr_mc_event(xdrs, event) &&
|
|
xdr_enumerate(xdrs, &n_volumes, MonitorClient::xdr_volume) &&
|
|
xdr_enumerate(xdrs, &n_devices, MonitorClient::xdr_device) &&
|
|
xdr_enumerate(xdrs, &n_partitions, MonitorClient::xdr_partition));
|
|
}
|
|
|
|
bool_t
|
|
MonitorClient::xdr_enumerate(XDR *xdrs,
|
|
unsigned int *np,
|
|
bool_t (MonitorClient::*enumerated)(XDR *, unsigned int))
|
|
{
|
|
unsigned int i;
|
|
|
|
if (xdr_u_int(xdrs, np))
|
|
for (i = 0; i < *np; i++)
|
|
if (!(this->*enumerated)(xdrs, i))
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
bool_t
|
|
MonitorClient::xdr_volume(XDR *xdrs, unsigned int index)
|
|
{
|
|
assert(xdrs->x_op == XDR_ENCODE); // Don't know how to do anything else
|
|
|
|
// Find the right volume.
|
|
|
|
Volume *volp = nth_volume(index);
|
|
assert(volp);
|
|
|
|
// Build an mc_volume structure. Note that we're violating
|
|
// constness, but since x_op is XDR_ENCODE, it's okay.
|
|
|
|
mc_volume vol;
|
|
bzero(&vol, sizeof vol);
|
|
vol.mcv_label = (char *) (volp->label() ? volp->label() : "");
|
|
vol.mcv_fsname = (char *) volp->fsname();
|
|
vol.mcv_dir = (char *) volp->dir();
|
|
vol.mcv_type = (char *) volp->type();
|
|
vol.mcv_subformat = (char *) (volp->subformat() ? volp->subformat() : "");
|
|
vol.mcv_mntopts = (char *) volp->options();
|
|
vol.mcv_mounted = volp->is_mounted();
|
|
if (volp->forced_unmount()) // this is a forced unmount event...
|
|
vol.mcv_mounted |= MC_VOL_FORCEUNMOUNT;
|
|
vol.mcv_exported = volp->is_shared();
|
|
|
|
// Transmit it.
|
|
|
|
my_xdr_mc_closure *mxmc = (my_xdr_mc_closure *) xdrs->x_public;
|
|
mxmc->vol = volp;
|
|
return xdr_mc_volume(xdrs, &vol);
|
|
}
|
|
|
|
bool_t
|
|
MonitorClient::xdr_volpartptrs(XDR *xdrs, u_int *, mc_partition ***)
|
|
{
|
|
assert(xdrs->x_op == XDR_ENCODE);
|
|
|
|
const Partition *pp;
|
|
|
|
my_xdr_mc_closure *mxmc = (my_xdr_mc_closure *) xdrs->x_public;
|
|
Volume *vol = mxmc->vol;
|
|
unsigned int nparts = vol->npartitions();
|
|
|
|
// Translate it.
|
|
|
|
if (!xdr_u_int(xdrs, &nparts))
|
|
return FALSE;
|
|
unsigned int ncheck = 0;
|
|
for (unsigned int i = 0; pp = vol->partition(i); i++)
|
|
{ unsigned int index = mxmc->client->count_partitions(pp);
|
|
if (!xdr_u_int(xdrs, &index))
|
|
return FALSE;
|
|
ncheck++;
|
|
}
|
|
assert(ncheck == nparts);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
bool_t
|
|
MonitorClient::xdr_device(XDR *xdrs, unsigned int index)
|
|
{
|
|
assert(xdrs->x_op == XDR_ENCODE); // Don't know how to do anything else
|
|
|
|
// Find the right device.
|
|
|
|
Device *dp = nth_device(index);
|
|
assert(dp);
|
|
DeviceMonitor *dmp = DeviceMonitor::at(dp);
|
|
|
|
// Build an mc_device structure. Note that we're violating
|
|
// constness, but since x_op is XDR_ENCODE, it's okay.
|
|
|
|
mc_device dev;
|
|
bzero(&dev, sizeof dev);
|
|
dev.mcd_name = (char *) dp->name();
|
|
dev.mcd_fullname = (char *) dp->full_name();
|
|
dev.mcd_ftrname = (char *) dp->ftr_name();
|
|
dev.mcd_devname = (char *) dp->dev_name(FMT_RAW,
|
|
PartitionAddress::WholeDisk);
|
|
|
|
dev.mcd_invent = dp->inventory();
|
|
dev.mcd_monitored = ((dmp->is_suspended() ? MC_DEV_SUSPENDED : 0) |
|
|
(dp->is_ignored() ? 0 : MC_DEV_NOTIGNORED));
|
|
dev.mcd_media_present = dmp ? dmp->is_media_present() : false;
|
|
dev.mcd_write_protected = dmp ? dmp->is_write_protected() : false;
|
|
dev.mcd_shared = dp->is_shared();
|
|
|
|
// Transmit it.
|
|
|
|
my_xdr_mc_closure *mxmc = (my_xdr_mc_closure *) xdrs->x_public;
|
|
mxmc->dev = dp;
|
|
return xdr_mc_device(xdrs, &dev);
|
|
}
|
|
|
|
bool_t
|
|
MonitorClient::xdr_devpartptrs(XDR *xdrs, u_int *, mc_partition ***)
|
|
{
|
|
assert(xdrs->x_op == XDR_ENCODE);
|
|
Partition *pp;
|
|
unsigned int nparts;
|
|
|
|
my_xdr_mc_closure *mxmc = (my_xdr_mc_closure *) xdrs->x_public;
|
|
Device *dev = mxmc->dev;
|
|
|
|
// Look at all partitions. Count the ones for this device.
|
|
|
|
nparts = 0;
|
|
for (pp = Partition::first(); pp; pp = pp->next())
|
|
if (pp->device() == dev)
|
|
nparts++;
|
|
|
|
// Translate the partition count, then translate all partitions.
|
|
|
|
if (!xdr_u_int(xdrs, &nparts))
|
|
return FALSE;
|
|
unsigned int ncheck = 0;
|
|
for (pp = Partition::first(); pp; pp = pp->next())
|
|
if (pp->device() == dev)
|
|
{ unsigned int index = mxmc->client->count_partitions(pp);
|
|
if (!xdr_u_int(xdrs, &index))
|
|
return FALSE;
|
|
ncheck++;
|
|
}
|
|
assert(ncheck == nparts);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
bool_t
|
|
MonitorClient::xdr_partition(XDR *xdrs, unsigned int index)
|
|
{
|
|
assert(xdrs->x_op == XDR_ENCODE); // Don't know how to do anything else
|
|
|
|
// Find the right partition.
|
|
|
|
const Partition *p = nth_partition(index);
|
|
assert(p);
|
|
|
|
// Build an mc_partition structure.
|
|
|
|
mc_partition part;
|
|
part.mcp_device = NULL;
|
|
part.mcp_format = (char *) p->format_name();
|
|
part.mcp_index = p->address().partition();
|
|
part.mcp_sectorsize = p->sector_size();
|
|
part.mcp_sector0 = p->start_sector();
|
|
part.mcp_nsectors = p->n_sectors();
|
|
|
|
// Transmit it.
|
|
|
|
my_xdr_mc_closure *mxmc = (my_xdr_mc_closure *) xdrs->x_public;
|
|
mxmc->dev = p->device();
|
|
return xdr_mc_partition(xdrs, &part);
|
|
}
|
|
|
|
bool_t
|
|
MonitorClient::xdr_devptr(XDR *xdrs, mc_device **)
|
|
{
|
|
assert(xdrs->x_op == XDR_ENCODE);
|
|
my_xdr_mc_closure *mxmc = (my_xdr_mc_closure *) xdrs->x_public;
|
|
Device *dev = mxmc->dev;
|
|
unsigned int index = mxmc->client->count_devices(dev);
|
|
return xdr_u_int(xdrs, &index);
|
|
}
|
|
bool_t
|
|
xdr_string(register XDR *xdrs, char **cpp, u_int maxsize)
|
|
{
|
|
register char *sp = *cpp; /* sp is the actual string pointer */
|
|
u_int size;
|
|
u_int nodesize;
|
|
|
|
/*
|
|
* first deal with the length since xdr strings are counted-strings
|
|
*/
|
|
switch (xdrs->x_op) {
|
|
case XDR_FREE:
|
|
if (sp == NULL) {
|
|
return(TRUE); /* already free */
|
|
}
|
|
/* fall through... */
|
|
case XDR_ENCODE:
|
|
size = (int)strlen(sp);
|
|
break;
|
|
}
|
|
if (! xdr_u_int(xdrs, &size)) {
|
|
return (FALSE);
|
|
}
|
|
if (size > maxsize) {
|
|
return (FALSE);
|
|
}
|
|
nodesize = size + 1;
|
|
|
|
/*
|
|
* now deal with the actual bytes
|
|
*/
|
|
switch (xdrs->x_op) {
|
|
|
|
case XDR_DECODE:
|
|
if (nodesize == 0) {
|
|
return (TRUE);
|
|
}
|
|
if (sp == NULL) {
|
|
*cpp = sp = (char *)mem_alloc(nodesize);
|
|
}
|
|
if (sp == NULL) {
|
|
int enough_memory = 0; assert(enough_memory);
|
|
return (FALSE);
|
|
}
|
|
sp[size] = 0;
|
|
/* fall into ... */
|
|
|
|
case XDR_ENCODE:
|
|
return (xdr_opaque(xdrs, sp, size));
|
|
|
|
case XDR_FREE:
|
|
mem_free(sp, nodesize);
|
|
*cpp = NULL;
|
|
return (TRUE);
|
|
}
|
|
return (FALSE);
|
|
}
|