Page MenuHomePhorge

quota.c
No OneTemporary

Authored By
Unknown
Size
12 KB
Referenced Files
None
Subscribers
None
/* quota.c -- program to report/reconstruct quotas
*
* Copyright (c) 1998-2000 Carnegie Mellon University. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "Carnegie Mellon University" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For permission or any other legal
* details, please contact
* Office of Technology Transfer
* Carnegie Mellon University
* 5000 Forbes Avenue
* Pittsburgh, PA 15213-3890
* (412) 268-4387, fax: (412) 268-7395
* tech-transfer@andrew.cmu.edu
*
* 4. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Computing Services
* at Carnegie Mellon University (http://www.cmu.edu/computing/)."
*
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
/* $Id: quota.c,v 1.40 2001/08/16 20:52:08 ken3 Exp $ */
#include <config.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <syslog.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/stat.h>
#include <com_err.h>
#if HAVE_DIRENT_H
# include <dirent.h>
# define NAMLEN(dirent) strlen((dirent)->d_name)
#else
# define dirent direct
# define NAMLEN(dirent) (dirent)->d_namlen
# if HAVE_SYS_NDIR_H
# include <sys/ndir.h>
# endif
# if HAVE_SYS_DIR_H
# include <sys/dir.h>
# endif
# if HAVE_NDIR_H
# include <ndir.h>
# endif
#endif
#include "assert.h"
#include "imapconf.h"
#include "exitcodes.h"
#include "imap_err.h"
#include "mailbox.h"
#include "xmalloc.h"
#include "mboxlist.h"
#include "mboxname.h"
#include "convert_code.h"
extern int errno;
extern int optind;
extern char *optarg;
/* current namespace */
static struct namespace quota_namespace;
/* forward declarations */
void usage(void);
void reportquota(void);
int buildquotalist(char **roots, int nroots);
int fixquota_mailbox(char *name,
int matchlen,
int maycreate,
void* rock);
int fixquota(int ispartial);
int fixquota_fixroot(struct mailbox *mailbox,
char *root);
int fixquota_finish(int thisquota);
struct quotaentry {
struct quota quota;
int refcount;
int deleted;
unsigned long newused;
};
#define QUOTAGROW 300
struct quotaentry zeroquotaentry;
struct quotaentry *quota;
int quota_num = 0, quota_alloc = 0;
int firstquota;
int redofix;
int partial;
int main(int argc,char **argv)
{
int opt;
int fflag = 0;
int r, code = 0;
char *alt_config = NULL;
if (geteuid() == 0) fatal("must run as the Cyrus user", EC_USAGE);
while ((opt = getopt(argc, argv, "C:f")) != EOF) {
switch (opt) {
case 'C': /* alt config file */
alt_config = optarg;
break;
case 'f':
fflag = 1;
break;
default:
usage();
}
}
config_init(alt_config, "quota");
/* Set namespace -- force standard (internal) */
if ((r = mboxname_init_namespace(&quota_namespace, 1)) != 0) {
syslog(LOG_ERR, error_message(r));
fatal(error_message(r), EC_CONFIG);
}
r = buildquotalist(argv+optind, argc-optind);
if (!r && fflag) {
mboxlist_init(0);
r = fixquota(argc-optind);
mboxlist_done();
}
if (!r) reportquota();
if (r) {
com_err("quota", r, (r == IMAP_IOERROR) ? error_message(errno) : NULL);
code = convert_code(r);
}
exit(code);
return 0;
}
void usage(void)
{
fprintf(stderr, "usage: quota [-C <alt_config>] [-f] [prefix]...\n");
exit(EC_USAGE);
}
/*
* Comparison function for sorting quota roots
*/
int compare_quota(const void *a, const void *b)
{
return strcmp(((struct quotaentry *)a)->quota.root,
((struct quotaentry *)b)->quota.root);
}
/*
* Build the list of quota roots in 'quota'
*/
int buildquotalist(char **roots, int nroots)
{
int r;
char quota_path[MAX_MAILBOX_PATH];
int i;
DIR *dirp;
DIR *topp;
struct dirent *dirent;
/* Translate separator in mailboxnames.
*
* We do this directly instead of using the mboxname_tointernal()
* function pointer because we know that we are using the internal
* namespace and so we don't have to allocate a buffer for the
* translated name.
*/
for (i = 0; i < nroots; i++) {
mboxname_hiersep_tointernal(&quota_namespace, roots[i]);
}
sprintf(quota_path, "%s%s", config_dir, FNAME_QUOTADIR);
if (chdir(quota_path)) {
return IMAP_IOERROR;
}
topp = opendir(".");
if (!topp) {
return IMAP_IOERROR;
}
while ((dirent = readdir(topp))!=NULL) {
if (dirent->d_name[0] == '.') continue;
dirp = opendir(dirent->d_name);
if (!dirp) continue;
while ((dirent = readdir(dirp))!=NULL) {
if (dirent->d_name[0] == '.') continue;
/* If restricting our list, see if this quota file matches */
if (nroots) {
for (i = 0; i < nroots; i++) {
if (!strcmp(dirent->d_name, roots[i]) ||
(!strncmp(dirent->d_name, roots[i], strlen(roots[i])) &&
dirent->d_name[strlen(roots[i])] == '.')) break;
}
if (i == nroots) continue;
}
/* Ignore .NEW files */
i = strlen(dirent->d_name);
if (i > 4 && !strcmp(dirent->d_name+i-4, ".NEW")) continue;
if (quota_num == quota_alloc) {
quota_alloc += QUOTAGROW;
quota = (struct quotaentry *)
xrealloc((char *)quota, quota_alloc * sizeof(struct quotaentry));
}
quota[quota_num] = zeroquotaentry;
quota[quota_num].quota.fd = -1;
quota[quota_num].quota.root = xstrdup(dirent->d_name);
r = mailbox_read_quota(&quota[quota_num].quota);
if (quota[quota_num].quota.fd != -1) {
close(quota[quota_num].quota.fd);
quota[quota_num].quota.fd = -1;
}
if (r) {
com_err(dirent->d_name, r,
(r == EC_IOERR) ? error_message(errno) : NULL);
quota[quota_num].quota.used = 0;
quota[quota_num].quota.limit = -1;
}
quota_num++;
}
/* close this subdirectory */
closedir(dirp);
}
closedir(topp);
qsort((char *)quota, quota_num, sizeof(*quota), compare_quota);
return 0;
}
/*
* Account for mailbox 'name' when fixing the quota roots
*/
int fixquota_mailbox(char *name,
int matchlen,
int maycreate,
void* rock)
{
int r;
struct mailbox mailbox;
int i, len, thisquota, thisquotalen;
while (firstquota < quota_num &&
strncmp(name, quota[firstquota].quota.root,
strlen(quota[firstquota].quota.root)) > 0) {
r = fixquota_finish(firstquota++);
if (r) return r;
}
thisquota = -1;
thisquotalen = 0;
for (i = firstquota;
i < quota_num && strcmp(name, quota[i].quota.root) >= 0; i++) {
len = strlen(quota[i].quota.root);
if (!strncmp(name, quota[i].quota.root, len) &&
(!name[len] || name[len] == '.')) {
quota[i].refcount++;
if (len > thisquotalen) {
thisquota = i;
thisquotalen = len;
}
}
}
if (partial && thisquota == -1) return 0;
r = mailbox_open_header(name, 0, &mailbox);
if (r) return r;
if (thisquota == -1) {
if (mailbox.quota.root) {
r = fixquota_fixroot(&mailbox, (char *)0);
if (r) {
mailbox_close(&mailbox);
return r;
}
}
mailbox_close(&mailbox);
return 0;
}
if (!mailbox.quota.root ||
strcmp(mailbox.quota.root, quota[thisquota].quota.root) != 0) {
r = fixquota_fixroot(&mailbox, quota[thisquota].quota.root);
if (r) {
mailbox_close(&mailbox);
return r;
}
}
if (quota[thisquota].quota.fd == -1) {
r = mailbox_lock_quota(&quota[thisquota].quota);
if (r) {
mailbox_close(&mailbox);
return r;
}
}
r = mailbox_open_index(&mailbox);
if (r) {
mailbox_close(&mailbox);
return r;
}
quota[thisquota].newused += mailbox.quota_mailbox_used;
mailbox_close(&mailbox);
return 0;
}
int fixquota_fixroot(struct mailbox *mailbox,
char *root)
{
int i, r;
/*
* Locking order is to lock header before quota. We therefore
* unlock all the quota roots we have locked in order to avoid a
* deadlock. As releasing these locks can cause the quota use
* recalculation to screw up, we set the global variable 'redofix'
* to cause the quota use recalculation to be redone.
*
* We could optimize this by trying to get a nonblocking lock on
* the header and unlocking all the quota roots only when that fails.
*/
for (i = firstquota; i < quota_num; i++) {
if (quota[i].quota.fd != -1) {
close(quota[i].quota.fd);
quota[i].quota.fd = -1;
}
}
redofix = 1;
r = mailbox_lock_header(mailbox);
if (r) return r;
printf("%s: quota root %s --> %s\n", mailbox->name,
mailbox->quota.root ? mailbox->quota.root : "(none)",
root ? root : "(none)");
if (mailbox->quota.root) free(mailbox->quota.root);
if (root) {
mailbox->quota.root = xstrdup(root);
}
else {
mailbox->quota.root = 0;
}
r = mailbox_write_header(mailbox);
(void) mailbox_unlock_header(mailbox);
return r;
}
/*
* Finish fixing up a quota root
*/
int fixquota_finish(int thisquota)
{
int r;
if (!quota[thisquota].refcount) {
if (!quota[thisquota].deleted++) {
printf("%s: removed\n", quota[thisquota].quota.root);
unlink(quota[thisquota].quota.root);
}
return 0;
}
if (quota[thisquota].quota.fd == -1) {
r = mailbox_lock_quota(&quota[thisquota].quota);
if (r) {
if (quota[thisquota].quota.fd != -1) {
close(quota[thisquota].quota.fd);
quota[thisquota].quota.fd = -1;
}
return r;
}
}
if (quota[thisquota].quota.used != quota[thisquota].newused) {
printf("%s: usage was %lu, now %lu\n", quota[thisquota].quota.root,
quota[thisquota].quota.used, quota[thisquota].newused);
quota[thisquota].quota.used = quota[thisquota].newused;
r = mailbox_write_quota(&quota[thisquota].quota);
if (r) return r;
}
close(quota[thisquota].quota.fd);
quota[thisquota].quota.fd = -1;
return 0;
}
/*
* Fix all the quota roots
*/
int fixquota(int ispartial)
{
int r = 0;
static char pattern[2] = "*";
/*
* Lock mailbox list to prevent mailbox creation/deletion
* during the fix
*/
mboxlist_open(NULL);
redofix = 1;
while (redofix) {
redofix = 0;
firstquota = 0;
partial = ispartial;
r = (*quota_namespace.mboxlist_findall)(&quota_namespace, pattern, 1,
0, 0, fixquota_mailbox, NULL);
if (r) {
mboxlist_close();
return r;
}
while (firstquota < quota_num) {
r = fixquota_finish(firstquota++);
if (r) {
mboxlist_close();
return r;
}
}
}
mboxlist_close();
return 0;
}
/*
* Print out the quota report
*/
void
reportquota(void)
{
int i;
char buf[MAX_MAILBOX_PATH];
printf(" Quota %% Used Used Root\n");
for (i = 0; i < quota_num; i++) {
if (quota[i].deleted) continue;
if (quota[i].quota.limit > 0) {
printf(" %7d %7ld", quota[i].quota.limit,
((quota[i].quota.used / QUOTA_UNITS) * 100) / quota[i].quota.limit);
}
else if (quota[i].quota.limit == 0) {
printf(" 0 ");
}
else {
printf(" ");
}
/* Convert internal name to external */
(*quota_namespace.mboxname_toexternal)(&quota_namespace,
quota[i].quota.root,
"cyrus", buf);
printf(" %7ld %s\n", quota[i].quota.used / QUOTA_UNITS, buf);
}
}
void fatal(const char* s, int code)
{
fprintf(stderr, "quota: %s\n", s);
exit(code);
}

File Metadata

Mime Type
text/x-c
Expires
Fri, Apr 24, 10:15 AM (1 d, 19 h ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18749217
Default Alt Text
quota.c (12 KB)

Event Timeline