diff -crN dovecot-1.0.beta2/src/lib/failures.c dovecot-1.0.beta2.mail.location+quota/src/lib/failures.c *** dovecot-1.0.beta2/src/lib/failures.c Tue Jan 31 16:18:19 2006 --- dovecot-1.0.beta2.mail.location+quota/src/lib/failures.c Tue Jan 31 16:19:08 2006 *************** *** 219,224 **** --- 219,229 ---- va_end(args); } + void i_vinfo(const char *format, va_list args) + { + info_handler(format, args); + } + void i_set_panic_handler(failure_callback_t *callback __attr_noreturn__) { if (callback == NULL) diff -crN dovecot-1.0.beta2/src/lib/failures.h dovecot-1.0.beta2.mail.location+quota/src/lib/failures.h *** dovecot-1.0.beta2/src/lib/failures.h Tue Jan 31 16:18:23 2006 --- dovecot-1.0.beta2.mail.location+quota/src/lib/failures.h Tue Jan 31 16:19:20 2006 *************** *** 22,27 **** --- 22,28 ---- void i_error(const char *format, ...) __attr_format__(1, 2); void i_warning(const char *format, ...) __attr_format__(1, 2); void i_info(const char *format, ...) __attr_format__(1, 2); + void i_vinfo(const char *format, va_list args); void i_fatal_status(int status, const char *format, ...) __attr_format__(2, 3) __attr_noreturn__; diff -crN dovecot-1.0.beta2/src/plugins/quota/Makefile.am dovecot-1.0.beta2.mail.location+quota/src/plugins/quota/Makefile.am *** dovecot-1.0.beta2/src/plugins/quota/Makefile.am Wed Dec 14 22:50:01 2005 --- dovecot-1.0.beta2.mail.location+quota/src/plugins/quota/Makefile.am Fri Feb 3 13:56:11 2006 *************** *** 2,10 **** -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-mail \ ! -I$(top_srcdir)/src/lib-storage ! lib01_quota_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib01_quota_plugin.la --- 2,11 ---- -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-mail \ ! -I$(top_srcdir)/src/lib-storage \ ! -I$(top_srcdir)/src/lib-index ! lib01_quota_plugin_la_LDFLAGS = -module -avoid-version -lrpcsvc module_LTLIBRARIES = \ lib01_quota_plugin.la *************** *** 15,21 **** quota-dict.c \ quota-dirsize.c \ quota-plugin.c \ ! quota-storage.c noinst_HEADERS = \ quota.h \ --- 16,23 ---- quota-dict.c \ quota-dirsize.c \ quota-plugin.c \ ! quota-storage.c \ ! quota-rquotad.c noinst_HEADERS = \ quota.h \ diff -crN dovecot-1.0.beta2/src/plugins/quota/Makefile.in dovecot-1.0.beta2.mail.location+quota/src/plugins/quota/Makefile.in *** dovecot-1.0.beta2/src/plugins/quota/Makefile.in Tue Jan 31 16:40:29 2006 --- dovecot-1.0.beta2.mail.location+quota/src/plugins/quota/Makefile.in Fri Feb 3 13:56:21 2006 *************** *** 60,66 **** LTLIBRARIES = $(module_LTLIBRARIES) lib01_quota_plugin_la_LIBADD = am_lib01_quota_plugin_la_OBJECTS = quota.lo quota-fs.lo quota-dict.lo \ ! quota-dirsize.lo quota-plugin.lo quota-storage.lo lib01_quota_plugin_la_OBJECTS = $(am_lib01_quota_plugin_la_OBJECTS) DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp --- 60,67 ---- LTLIBRARIES = $(module_LTLIBRARIES) lib01_quota_plugin_la_LIBADD = am_lib01_quota_plugin_la_OBJECTS = quota.lo quota-fs.lo quota-dict.lo \ ! quota-dirsize.lo quota-plugin.lo quota-storage.lo \ ! quota-rquotad.lo lib01_quota_plugin_la_OBJECTS = $(am_lib01_quota_plugin_la_OBJECTS) DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp *************** *** 205,213 **** -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-mail \ ! -I$(top_srcdir)/src/lib-storage ! lib01_quota_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ lib01_quota_plugin.la --- 206,215 ---- -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-mail \ ! -I$(top_srcdir)/src/lib-storage \ ! -I$(top_srcdir)/src/lib-index ! lib01_quota_plugin_la_LDFLAGS = -module -avoid-version -lrpcsvc module_LTLIBRARIES = \ lib01_quota_plugin.la *************** *** 217,223 **** quota-dict.c \ quota-dirsize.c \ quota-plugin.c \ ! quota-storage.c noinst_HEADERS = \ quota.h \ --- 219,226 ---- quota-dict.c \ quota-dirsize.c \ quota-plugin.c \ ! quota-storage.c \ ! quota-rquotad.c noinst_HEADERS = \ quota.h \ *************** *** 298,303 **** --- 301,307 ---- @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/quota-dirsize.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/quota-fs.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/quota-plugin.Plo@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/quota-rquotad.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/quota-storage.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/quota.Plo@am__quote@ diff -crN dovecot-1.0.beta2/src/plugins/quota/quota-rquotad.c dovecot-1.0.beta2.mail.location+quota/src/plugins/quota/quota-rquotad.c *** dovecot-1.0.beta2/src/plugins/quota/quota-rquotad.c Thu Jan 1 01:00:00 1970 --- dovecot-1.0.beta2.mail.location+quota/src/plugins/quota/quota-rquotad.c Fri Feb 3 13:55:56 2006 *************** *** 0 **** --- 1,500 ---- + /* Copyright (C) 2005 Thomas Wouters */ + + /* Quota reporting based on rquotad, loosely based on FreeBSD's quota(1) + program */ + + #include "lib.h" + #include "str.h" + #include "index/index-mail.h" + #include "index/index-storage.h" + #include "quota-private.h" + + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + struct rquotad_quota { + struct quota quota; + + pool_t pool; + const char *error; + + size_t nroots; + struct rquotad_quota_root *roots; + + }; + + enum rquotad_states { RQUOTAD_UNKNOWN, RQUOTAD_ERR, RQUOTAD_OFF, RQUOTAD_ON }; + + struct rquotad_quota_root { + struct quota_root root; + + pool_t pool; + const char *name; + enum rquotad_states state; + const char **resources; + uint64_t kbyte_usage; + uint64_t inode_usage; + uint64_t kbyte_limit; + uint64_t inode_limit; + }; + + struct rquotad_quota_root_iter { + struct quota_root_iter iter; + char boxfs[MNAMELEN]; + size_t offset; + }; + + static void debugmsg(const char *msg, ...) + { + va_list args; + static int debug = -1; + if (debug < 0) { + if (getenv("DEBUG") != NULL) + debug = 1; + else + debug = 0; + } + if (!debug) + return; + + va_start(args, msg); + i_vinfo(msg, args); + va_end(args); + } + + extern struct quota rquotad_quota; + static void rquotad_quota_root_init(struct rquotad_quota_root *root, + const char *name, + struct rquotad_quota *quota); + static void rquotad_quota_root_update(struct rquotad_quota_root *root); + + static struct quota *rquotad_quota_init(const char *data) + { + struct rquotad_quota *quota; + const char *const *qroots; + size_t qr_idx; + pool_t pool; + + pool = pool_alloconly_create("quota", 1024); + quota = p_new(pool, struct rquotad_quota, 1); + quota->pool = pool; + quota->quota = rquotad_quota; + + debugmsg("starting quota init"); + qroots = t_strsplit(data, ","); + for (qr_idx = 0; qroots[qr_idx] != NULL; qr_idx++) { + /* NOTHING */; + } + debugmsg("found %d quotaroots", qr_idx); + quota->roots = p_new(pool, struct rquotad_quota_root, qr_idx); + quota->nroots = qr_idx; + for (qr_idx = 0; qroots[qr_idx] != NULL; qr_idx++) { + rquotad_quota_root_init(&(quota->roots[qr_idx]), + qroots[qr_idx], quota); + } + return "a->quota; + } + + static void rquotad_quota_deinit(struct quota *_quota) + { + struct rquotad_quota *quota = (struct rquotad_quota *)_quota; + + pool_unref(quota->pool); + } + + static void rquotad_quota_root_init(struct rquotad_quota_root *root, + const char *name, + struct rquotad_quota *quota) + { + debugmsg("creating quotaroot %s", name); + root->name = p_strdup(quota->pool, name); + root->root.quota = (struct quota *)quota; + root->pool = quota->pool; + root->state = RQUOTAD_UNKNOWN; + } + + static struct quota_root_iter * + rquotad_quota_root_iter_init(struct quota *quota, + struct mailbox *box) + { + struct rquotad_quota_root_iter *iter; + struct statfs statbuf; + + if (statfs(((struct index_storage *)(mailbox_get_storage(box)))->dir, &statbuf) < 0) { + return NULL; + } + + debugmsg("getting quota for %s (%s)", box->name, + mailbox_get_storage(box)->name); + iter = i_new(struct rquotad_quota_root_iter, 1); + iter->iter.quota = quota; + strncpy(iter->boxfs, statbuf.f_mntonname, MNAMELEN); + debugmsg("mntonname %s", iter->boxfs); + return &iter->iter; + } + + static struct quota_root * + rquotad_quota_root_iter_next(struct quota_root_iter *_iter) + { + struct rquotad_quota_root_iter *iter = + (struct rquotad_quota_root_iter *)_iter; + struct rquotad_quota *quota = (struct rquotad_quota *)_iter->quota; + struct rquotad_quota_root *root; + + debugmsg("Iter step %d, nroots %d", iter->offset, quota->nroots); + for (; iter->offset < quota->nroots; iter->offset++) { + root = "a->roots[iter->offset]; + debugmsg("comparing root %d (%s)", iter->offset, root->name); + if (strcmp(iter->boxfs, root->name) == 0) { + debugmsg("Returning root %d", iter->offset); + iter->offset++; + return (struct quota_root *) root; + } + } + return NULL; + } + + static int rquotad_quota_root_iter_deinit(struct quota_root_iter *iter) + { + i_free(iter); + return 0; + } + + static struct quota_root * + rquotad_quota_root_lookup(struct quota *_quota, const char *name) + { + struct rquotad_quota *quota = (struct rquotad_quota *)_quota; + struct rquotad_quota_root *root; + size_t i; + + for (i = 0; i < quota->nroots; i++) { + root = "a->roots[i]; + if (strcmp(root->name, name) == 0) { + return (struct quota_root *)root; + } + } + return NULL; + } + + static const char * + rquotad_quota_root_get_name(struct quota_root *_root) + { + struct rquotad_quota_root *root = (struct rquotad_quota_root *)_root; + return root->name; + } + + static const char *const * + rquotad_quota_root_get_resources(struct quota_root *_root) + { + struct rquotad_quota_root *root = (struct rquotad_quota_root *)_root; + int i = 0; + + if (root->resources != NULL) { + return root->resources; + } + if (root->state == RQUOTAD_UNKNOWN) { + rquotad_quota_root_update(root); + } + if (root->state == RQUOTAD_ERR) { + return NULL; + } + if (root->state == RQUOTAD_OFF) { + root->resources = p_new(root->pool, const char *, 1); + root->resources[0] = NULL; + return root->resources; + } + /* Out of date is alright, we don't really care about up-to-date + * numbers, just whether quotas are active */ + root->resources = p_new(root->pool, const char *, 3); + if (root->kbyte_limit != 0) { + root->resources[i++] = QUOTA_NAME_STORAGE; + } + if (root->inode_limit != 0) { + root->resources[i++] = QUOTA_NAME_MESSAGES; + } + root->resources[i] = NULL; + + return root->resources; + } + + static int + rquotad_quota_root_create(struct quota *_quota, + const char *name __attr_unused__, + struct quota_root **root_r __attr_unused__) + { + struct rquotad_quota *quota = (struct rquotad_quota *)_quota; + + quota->error = "Permission denied"; + return -1; + } + + static int + rquotad_quota_get_resource(struct quota_root *_root, const char *name, + uint64_t *value_r, uint64_t *limit_r) + { + struct rquotad_quota_root *root = (struct rquotad_quota_root *)_root; + if (root->state == RQUOTAD_UNKNOWN || root->state == RQUOTAD_ON) { + rquotad_quota_root_update(root); + } + if (root->state == RQUOTAD_ERR) { + return -1; + } + *value_r = 0; + *limit_r = 0; + if (root->state == RQUOTAD_OFF) { + return 0; + } + if (strcmp(name, QUOTA_NAME_STORAGE) == 0) { + *value_r = root->kbyte_usage; + *limit_r = root->kbyte_limit; + return 1; + } + if (strcmp(name, QUOTA_NAME_MESSAGES) == 0) { + *value_r = root->inode_usage; + *limit_r = root->inode_limit; + return 1; + } + return 0; + } + + static int + rquotad_quota_set_resource(struct quota_root *root, + const char *name __attr_unused__, + uint64_t value __attr_unused__) + { + struct rquotad_quota *quota = (struct rquotad_quota *)root->quota; + + quota->error = "Permission denied"; + return -1; + } + + static struct quota_transaction_context * + rquotad_quota_transaction_begin(struct quota *quota) + { + struct quota_transaction_context *ctx; + + ctx = i_new(struct quota_transaction_context, 1); + ctx->quota = quota; + return ctx; + } + + static int + rquotad_quota_transaction_commit(struct quota_transaction_context *ctx) + { + i_free(ctx); + return 0; + } + + static void + rquotad_quota_transaction_rollback(struct quota_transaction_context *ctx) + { + i_free(ctx); + } + + static int + rquotad_quota_try_alloc(struct mailbox_transaction_context *ctx __attr_unused__, + struct quota *_quota __attr_unused__, + struct mail *mail __attr_unused__) + { + return 1; + } + + static void + rquotad_quota_alloc(struct mailbox_transaction_context *ctx __attr_unused__, + struct quota *quota __attr_unused__, + struct mail *mail __attr_unused__) + { + /* no-op */ + } + + static void + rquotad_quota_free(struct mailbox_transaction_context *ctx __attr_unused__, + struct quota *quota __attr_unused__, + struct mail *mail __attr_unused__) + { + /* no-op */ + } + + const char *rquotad_quota_last_error(struct quota *_quota) + { + struct rquotad_quota *quota = (struct rquotad_quota *)_quota; + + return quota->error; + } + + struct quota rquotad_quota = { + "rquotad", + + rquotad_quota_init, + rquotad_quota_deinit, + + rquotad_quota_root_iter_init, + rquotad_quota_root_iter_next, + rquotad_quota_root_iter_deinit, + + rquotad_quota_root_lookup, + + rquotad_quota_root_get_name, + rquotad_quota_root_get_resources, + + rquotad_quota_root_create, + rquotad_quota_get_resource, + rquotad_quota_set_resource, + + rquotad_quota_transaction_begin, + rquotad_quota_transaction_commit, + rquotad_quota_transaction_rollback, + + rquotad_quota_try_alloc, + rquotad_quota_alloc, + rquotad_quota_free, + + rquotad_quota_last_error, + + ARRAY_INIT + }; + + static int + rquota_get(char *host, getquota_args *qargs, getquota_rslt *qres) + { + struct sockaddr_in rhost; + struct hostent *hent; + struct timeval tout; + enum clnt_stat cstat; + CLIENT *clnt = NULL; + int sock = RPC_ANYSOCK; + + hent = gethostbyname(host); + if (!hent) + return -1; + if (hent->h_length > (int)sizeof(rhost.sin_addr)) + return -1; + + tout.tv_sec = 6; + tout.tv_usec = 0; + + *(long *)&rhost.sin_addr = *(long *)hent->h_addr; + rhost.sin_family = AF_INET; + rhost.sin_port = 0; + + clnt = clntudp_create(&rhost, RQUOTAPROG, RQUOTAVERS, tout, &sock); + if (!clnt) + return rpc_createerr.cf_stat; + + clnt->cl_auth = authunix_create_default(); + tout.tv_sec = 25; + tout.tv_usec = 0; + cstat = clnt_call(clnt, RQUOTAPROC_GETQUOTA, xdr_getquota_args, + qargs, xdr_getquota_rslt, qres, tout); + clnt_destroy(clnt); + return cstat; + } + + static void rquotad_quota_root_error_alloc(struct rquotad_quota_root *root, + const char *errmsg, ...) + { + struct rquotad_quota *quota = (struct rquotad_quota *)root->root.quota; + va_list args; + va_start(args, errmsg); + quota->error = p_strdup_vprintf(root->pool, errmsg, args); + va_end(args); + } + + static void rquotad_quota_root_update(struct rquotad_quota_root *root) + { + struct statfs statfsbuf; + struct getquota_args qargs; + struct getquota_rslt qres; + char *remotehost, *remotepath; + char *c; + + if (root->state == RQUOTAD_OFF) { + return; + } + + if (statfs(root->name, &statfsbuf) < 0) { + rquotad_quota_root_error_alloc(root, strerror(errno)); + root->state = RQUOTAD_ERR; + return; + } + + if (statfsbuf.f_flags & MNT_LOCAL) { + root->state = RQUOTAD_OFF; + return; + } + + /* + * must be some form of "hostname:/path" + */ + c = strchr(statfsbuf.f_mntfromname, ':'); + if (c == NULL) { + rquotad_quota_root_error_alloc(root, + "cannot find hostname for %s", + statfsbuf.f_mntfromname); + root->state = RQUOTAD_ERR; + return; + } + + *c = '\0'; + remotehost = strdup(statfsbuf.f_mntfromname); + remotepath = strdup(c + 1); + *c = ':'; + + if (remotepath[0] != '/') { + rquotad_quota_root_error_alloc(root, "weird filesystem %s", + statfsbuf.f_mntfromname); + root->state = RQUOTAD_ERR; + return; + } + + qargs.gqa_pathp = remotepath; + qargs.gqa_uid = getuid(); + + if (rquota_get(remotehost, &qargs, &qres) != 0) { + return; + } + + switch (qres.status) { + case Q_OK: + root->kbyte_limit = + (qres.getquota_rslt_u.gqr_rquota.rq_bhardlimit * + qres.getquota_rslt_u.gqr_rquota.rq_bsize) / 1024; + root->kbyte_usage = + (qres.getquota_rslt_u.gqr_rquota.rq_curblocks * + qres.getquota_rslt_u.gqr_rquota.rq_bsize) / 1024; + /* inodes */ + root->inode_limit = + qres.getquota_rslt_u.gqr_rquota.rq_fhardlimit; + root->inode_usage = + qres.getquota_rslt_u.gqr_rquota.rq_curfiles; + root->state = RQUOTAD_ON; + return; + case Q_NOQUOTA: + root->state = RQUOTAD_OFF; + return; + case Q_EPERM: + rquotad_quota_root_error_alloc(root, + "quota permission error, host/path %s/%s", + remotehost, remotepath); + root->state = RQUOTAD_ERR; + return; + } + rquotad_quota_root_error_alloc(root, + "bad rpc result %d, host/path %s/%s", + qres.status, remotehost, remotepath); + root->state = RQUOTAD_ERR; + } + + + diff -crN dovecot-1.0.beta2/src/plugins/quota/quota.c dovecot-1.0.beta2.mail.location+quota/src/plugins/quota/quota.c *** dovecot-1.0.beta2/src/plugins/quota/quota.c Fri Jan 13 21:20:18 2006 --- dovecot-1.0.beta2.mail.location+quota/src/plugins/quota/quota.c Wed Feb 1 00:00:09 2006 *************** *** 10,22 **** extern struct quota dirsize_quota; extern struct quota dict_quota; extern struct quota fs_quota; static struct quota *quota_classes[] = { &dirsize_quota, &dict_quota, #ifdef HAVE_FS_QUOTA ! &fs_quota #endif }; #define QUOTA_CLASS_COUNT (sizeof(quota_classes)/sizeof(quota_classes[0])) --- 10,24 ---- extern struct quota dirsize_quota; extern struct quota dict_quota; extern struct quota fs_quota; + extern struct quota rquotad_quota; static struct quota *quota_classes[] = { &dirsize_quota, &dict_quota, #ifdef HAVE_FS_QUOTA ! &fs_quota, #endif + &rquotad_quota, }; #define QUOTA_CLASS_COUNT (sizeof(quota_classes)/sizeof(quota_classes[0]))