|
From: Andrew T. <an...@to...> - 2012-01-24 00:37:48
|
On Mon, Jan 23, 2012 at 5:31 PM, Miklos Szeredi <mi...@sz...> wrote:
> Why do you need to install your own signal handler?
Because the signal handlers are already used by the rest of the
application. I have to take many more actions than just shutting down
the fuse event loop thread on receipt of certain signals.
Essentially my interface to the fuse library is wrapped as follows:
class FuseSystem
{
public:
// mount a filesystem at path mountpoint using the ops object
to handle the operations:
FuseSystem(const string& mountpoint, IFuseOperations& ops, bool
debug = false);
// loop until exit called
void loop();
// exit loop
void exit();
// unmount
~FuseSystem();
private:
...
};
FuseSystem::exit() should be able to be called from anywhere (another
thread or a signal handler) and it should shutdown loop cleanly and
immediately.
The object should be multi-instance capable, ie you should be able to
have multiple different FuseSystem instances (that access either the
same or different IFuseOperations instances) in different threads in
the same process. (Even better would be the option to have a shared
event loop and use non-blocking io such as select/poll to read the
/dev/fuse handles).
If you're curious here is IFuseOperations:
class IFuseOperations
{
public:
virtual void init(fuse_conn_info& conn) = 0;
virtual void destroy() = 0;
virtual fuse_entry_param mknod(fuse_ino_t parent, const string& name,
uid_t user, gid_t group, mode_t mode, dev_t rdev) = 0;
virtual void forget(fuse_ino_t ino, unsigned long nlookup) = 0;
virtual struct statvfs statfs() = 0;
virtual fuse_entry_param mkdir(fuse_ino_t parent, const string& name,
uid_t user, gid_t group, mode_t mode) = 0;
virtual void unlink(fuse_ino_t parent, const string& name) = 0;
virtual fuse_entry_param lookup(fuse_ino_t parent, const string& name) = 0;
virtual void rmdir(fuse_ino_t parent, const string& name) = 0;
virtual void rename(fuse_ino_t parent, const string& name, fuse_ino_t
newparent, const string& newname) = 0;
virtual fuse_entry_param link( fuse_ino_t newparent, const string&
newname, fuse_ino_t ino) = 0;
virtual fuse_entry_param symlink(fuse_ino_t parent, const string&
name, uid_t user, gid_t group, const string& link) = 0;
virtual void opendir(fuse_ino_t ino, fuse_file_info& fi) = 0;
virtual PBuffer readdir(fuse_ino_t ino, const fuse_file_info& fi,
off_t off, size_t size) = 0;
virtual void fsyncdir(fuse_ino_t ino, const fuse_file_info& fi, bool
datasync) = 0;
virtual void releasedir(fuse_ino_t ino, const fuse_file_info& fi) = 0;
virtual pair<struct stat, double> getattr(fuse_ino_t ino) = 0;
virtual pair<struct stat, double> setattr(fuse_ino_t ino, const
fuse_file_info& fi, int to_set, const struct stat& attr) = 0;
virtual void open(fuse_ino_t ino, fuse_file_info& fi) = 0;
virtual PBuffer read(fuse_ino_t ino, const fuse_file_info& fi, off_t
off, size_t size) = 0;
virtual size_t write(fuse_ino_t ino, const fuse_file_info& fi, off_t
off, Rom buf) = 0;
virtual void flush(fuse_ino_t ino, const fuse_file_info& fi) = 0;
virtual void release(fuse_ino_t ino, const fuse_file_info& fi) = 0;
virtual void fsync(fuse_ino_t ino, const fuse_file_info& fi, bool
datasync) = 0;
virtual string readlink(fuse_ino_t ino) = 0;
virtual ~IFuseOperations() {}
};
So the user just subclasses this, implements these methods and passes
an instance to the FuseSystem constructor.
Here is FuseSystem implementation:
// (C) 2011, Andrew Tomazos <an...@to...>. All Rights Reserved.
#include "Standard.pch"
#include "Fuse/FuseSystem.h"
#include "OperatingSystem/SystemError.h"
#include "OperatingSystem/StackTrace.h"
#include "OperatingSystem/Log.h"
#include "Fuse/FuseOrigin.h"
class FuseRequest : public FuseOrigin
{
public:
IFuseOperations& operations()
{
return ((FuseSystem*) fuse_req_userdata(m))->operations();
}
template<class F, class ... A>
void reply(F f, A&&... a)
{
int res = f(m, forward<A>(a)...);
if (res != 0)
{
Error("___ returned ___", FunctionString((void*)f), res);
}
}
// TODO: check errors
void err(int err_) { reply(fuse_reply_err, err_); }
void none() { fuse_reply_none(m); }
void entry(const fuse_entry_param& e) { reply(fuse_reply_entry, &e); }
void create(const fuse_entry_param& e, const fuse_file_info& fi) {
reply(fuse_reply_create, &e, &fi); }
void attr(const pair<struct stat, double>& attr) {
reply(fuse_reply_attr, &attr.first, attr.second); }
void readlink(const string& link) { reply(fuse_reply_readlink, link.c_str()); }
void open(const fuse_file_info& fi) { reply(fuse_reply_open, &fi); }
void write(size_t count) { reply(fuse_reply_write, count); }
void buf(const PBuffer& buf) { reply(fuse_reply_buf, buf->cbegin(),
buf->size()); }
void statfs(const struct statvfs& stbuf) { reply(fuse_reply_statfs, &stbuf); }
private:
FuseRequest(fuse_req_t req) : FuseOrigin(req) {}
friend class FuseSystemOperationsType;
};
struct FuseSystemOperationsType : public fuse_lowlevel_ops
{
static void cb_init(void *userdata, fuse_conn_info* conn) {
((FuseSystem*) userdata)->operations().init(*conn); }
static void cb_destroy(void *userdata) { ((FuseSystem*)
userdata)->operations().destroy(); }
static void cb_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
{
FuseRequest freq(req);
try { freq.entry(freq.operations().lookup(parent, name)); }
catch (int e) { freq.err(e); }
}
static void cb_forget(fuse_req_t req, fuse_ino_t ino, unsigned long nlookup)
{
FuseRequest freq(req);
freq.operations().forget(ino, nlookup);
freq.none();
}
static void cb_getattr(fuse_req_t req, fuse_ino_t ino, fuse_file_info*)
{
FuseRequest freq(req);
try { freq.attr(freq.operations().getattr(ino)); }
catch (int e) { freq.err(e); }
}
// TODO Add non-int exceptions
static void cb_setattr(fuse_req_t req, fuse_ino_t ino, struct stat* attr,
int to_set, fuse_file_info* fi)
{
FuseRequest freq(req);
try { freq.attr(freq.operations().setattr(ino, *fi, to_set, *attr)); }
catch (int e) { freq.err(e); }
}
static void cb_readlink(fuse_req_t req, fuse_ino_t ino)
{
FuseRequest freq(req);
try { freq.readlink(freq.operations().readlink(ino)); }
catch (int e) { freq.err(e); }
}
static void cb_mknod(fuse_req_t req, fuse_ino_t parent, const char* name,
mode_t mode, dev_t rdev)
{
FuseRequest freq(req);
try { freq.entry(freq.operations().mknod(parent, name, freq.user(),
freq.group(), mode, rdev)); }
catch (int e) { freq.err(e); }
}
static void cb_mkdir(fuse_req_t req, fuse_ino_t parent, const char*
name, mode_t mode)
{
FuseRequest freq(req);
try { freq.entry(freq.operations().mkdir(parent, name, freq.user(),
freq.group(), mode)); }
catch (int e) { freq.err(e); }
}
static void cb_unlink(fuse_req_t req, fuse_ino_t parent, const char* name)
{
FuseRequest freq(req);
try { freq.operations().unlink(parent, name); freq.err(0); }
catch (int e) { freq.err(e); }
}
static void cb_rmdir(fuse_req_t req, fuse_ino_t parent, const char* name)
{
FuseRequest freq(req);
try { freq.operations().rmdir(parent, name); freq.err(0); }
catch (int e) { freq.err(e); }
}
static void cb_symlink(fuse_req_t req, const char* link, fuse_ino_t parent,
const char* name)
{
FuseRequest freq(req);
try { freq.entry(freq.operations().symlink(parent, name,
freq.user(), freq.group(), link)); }
catch (int e) { freq.err(e); }
}
static void cb_rename(fuse_req_t req, fuse_ino_t parent, const char *name,
fuse_ino_t newparent, const char* newname)
{
FuseRequest freq(req);
try { freq.operations().rename(parent, name, newparent, newname);
freq.err(0); }
catch (int e) { freq.err(e); }
}
static void cb_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent,
const char* newname)
{
Log("cb_link");
FuseRequest freq(req);
try { freq.entry(freq.operations().link(newparent, newname, ino)); }
catch (int e) { freq.err(e); }
}
static void cb_open(fuse_req_t req, fuse_ino_t ino, fuse_file_info* fi)
{
FuseRequest freq(req);
try { freq.operations().open(ino, *fi); freq.open(*fi); }
catch (int e) { freq.err(e); }
}
static void cb_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
fuse_file_info* fi)
{
FuseRequest freq(req);
try { freq.buf(freq.operations().read(ino, *fi, off, size)); }
catch (int e) { freq.err(e); }
}
static void cb_write(fuse_req_t req, fuse_ino_t ino, const char* buf,
size_t size, off_t off, fuse_file_info* fi)
{
FuseRequest freq(req);
try { freq.write(freq.operations().write(ino, *fi, off, Rom(buf, size))); }
catch (int e) { freq.err(e); }
}
static void cb_flush(fuse_req_t req, fuse_ino_t ino, fuse_file_info* fi)
{
FuseRequest freq(req);
try { freq.operations().flush(ino, *fi); freq.err(0); }
catch (int e) { freq.err(e); }
}
static void cb_release(fuse_req_t req, fuse_ino_t ino, fuse_file_info* fi)
{
FuseRequest freq(req);
try { freq.operations().release(ino, *fi); freq.err(0); }
catch (int e) { freq.err(e); }
}
static void cb_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
fuse_file_info* fi)
{
FuseRequest freq(req);
try { freq.operations().fsync(ino, *fi, datasync); freq.err(0); }
catch (int e) { freq.err(e); }
}
static void cb_opendir(fuse_req_t req, fuse_ino_t ino, fuse_file_info* fi)
{
FuseRequest freq(req);
try { freq.operations().opendir(ino, *fi); freq.open(*fi); }
catch (int e) { freq.err(e); }
}
static void cb_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t off, fuse_file_info* fi)
{
FuseRequest freq(req);
try { freq.buf(freq.operations().readdir(ino, *fi, off, size)); }
catch (int e) { freq.err(e); }
}
static void cb_releasedir(fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info *fi)
{
FuseRequest freq(req);
try { freq.operations().releasedir(ino, *fi); freq.err(0); }
catch (int e) { freq.err(e); }
}
static void cb_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync,
struct fuse_file_info *fi)
{
FuseRequest freq(req);
try { freq.operations().fsyncdir(ino, *fi, datasync); freq.err(0); }
catch (int e) { freq.err(e); }
}
static void cb_statfs(fuse_req_t req, fuse_ino_t)
{
FuseRequest freq(req);
try { freq.statfs(freq.operations().statfs()); }
catch (int e) { freq.err(e); }
}
FuseSystemOperationsType();
};
FuseSystemOperationsType FuseSystemOperations;
FuseSystemOperationsType::FuseSystemOperationsType()
{
this->init = cb_init;
this->destroy = cb_destroy;
this->lookup = cb_lookup;
this->forget = cb_forget;
this->getattr = cb_getattr;
this->setattr = cb_setattr;
this->readlink = cb_readlink;
this->mknod = cb_mknod;
this->mkdir = cb_mkdir;
this->unlink = cb_unlink;
this->rmdir = cb_rmdir;
this->symlink = cb_symlink;
this->rename = cb_rename;
this->link = cb_link;
this->open = cb_open;
this->read = cb_read;
this->write = cb_write;
this->flush = cb_flush;
this->release = cb_release;
this->fsync = cb_fsync;
this->opendir = cb_opendir;
this->readdir = cb_readdir;
this->releasedir = cb_releasedir;
this->fsyncdir = cb_fsyncdir;
this->statfs = cb_statfs;
this->setxattr = nullptr;
this->getxattr = nullptr;
this->listxattr = nullptr;
this->removexattr = nullptr;
this->access = nullptr;
this->create = nullptr;
this->getlk = nullptr;
this->setlk = nullptr;
this->bmap = nullptr;
this->ioctl = nullptr;
this->poll = nullptr;
}
;
FuseSystem::FuseSystem(const string& mountpoint, IFuseOperations& ops,
bool debug) :
m_mountpoint(mountpoint),
m_channel(0),
m_session(0),
m_ops(ops),
m_args(FUSE_ARGS_INIT(0, NULL))
{
fuse_opt_add_arg(&m_args, m_mountpoint.c_str());
if (debug)
fuse_opt_add_arg(&m_args, "-d");
fuse_opt_add_arg(&m_args, "-o");
fuse_opt_add_arg(&m_args, "default_permissions");
if (fuse_parse_cmdline(&m_args, NULL, NULL, NULL))
throw runtime_error("fuse_parse_cmdline");
m_channel = fuse_mount(m_mountpoint.c_str(), &m_args);
if (m_channel == nullptr)
throw SystemError("fuse_mount(___)", m_mountpoint);
m_session = fuse_lowlevel_new(&m_args, &FuseSystemOperations,
sizeof(fuse_lowlevel_ops), this);
if (m_session == nullptr)
throw SystemError("fuse_lowlevel_new(___)", mountpoint);
fuse_session_add_chan(m_session, m_channel);
}
void FuseSystem::loop()
{
auto err = fuse_session_loop(m_session);
if (err)
throw runtime_error(Format("fuse_session_loop returned ", err));
}
void FuseSystem::exit()
{
fuse_session_exit(m_session);
}
FuseSystem::~FuseSystem()
{
if (m_session != nullptr && m_channel != nullptr)
fuse_session_remove_chan(m_channel);
if (m_session != nullptr)
fuse_session_destroy(m_session);
if (m_channel != nullptr)
fuse_unmount(m_mountpoint.c_str(), m_channel);
fuse_opt_free_args(&m_args);
}
Enjoy,
Andrew.
|