VFS accounting: add a new vquotactl() syscall
authorFrancois Tigeot <ftigeot@wolfpond.org>
Tue, 16 Aug 2011 10:01:27 +0000 (12:01 +0200)
committerFran├žois Tigeot <ftigeot@wolfpond.org>
Tue, 13 Dec 2011 21:30:41 +0000 (22:30 +0100)
* It uses a proplib(3) based interface to pass messages between
  kernel and userland

* It may be used to handle different commands in the future

* For now, only implement one displaying the contents of the
  space counters for a particular mount point

* Also add a debug flag to vquota(8) showing messages exchanged
  between kernel and userland

13 files changed:
sbin/vquota/Makefile
sbin/vquota/vquota.8
sbin/vquota/vquota.c
sys/kern/init_sysent.c
sys/kern/syscalls.c
sys/kern/syscalls.master
sys/kern/vfs_quota.c
sys/sys/syscall-hide.h
sys/sys/syscall.h
sys/sys/syscall.mk
sys/sys/sysproto.h
sys/sys/sysunion.h
sys/sys/vfs_quota.h

index 216afa9..62e1124 100644 (file)
@@ -1,5 +1,6 @@
 PROG=  vquota
 SRCS=  vquota.c
+LDADD= -lprop
 MAN=   vquota.8
 
 .include <bsd.prog.mk>
index 1ab1ddc..69e0d1e 100644 (file)
@@ -53,8 +53,22 @@ directory hierarchy present under
 .\" ==== lsfs ====
 .It Cm lsfs
 List mounted file systems having space accounting enabled
+.
+.\" ==== show ====
+.It Cm show Ar mount_point
+Show file size usage for the file system mounted under
+.Ar mount_point .
+The information is broken down by uid and gid
 .El
 .
+.Sh EXAMPLES
+The following is an example of a typical usage
+of the
+.Nm
+command:
+.Pp
+.Dl "vquota show /mnt/data"
+.
 .Sh HISTORY
 The
 .Nm
@@ -62,5 +76,3 @@ utility first appeared in
 .Dx 2.13 .
 .Sh AUTHORS
 .An Francois Tigeot Aq ftigeot@wolfpond.org
-.Sh BUGS
-This manual page is just a skeleton and should be updated.
index 3d2944e..334b340 100644 (file)
 
 #include <sys/stat.h>
 #include <sys/mount.h>
+#include <sys/vfs_quota.h>
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <err.h>
 #include <string.h>
 #include <fts.h>
+#include <libprop/proplib.h>
+#include <unistd.h>
 #include <inttypes.h>
 
+static bool flag_debug = 0;
+
 static void usage(int);
 static int get_dirsize(char *);
 static int get_fslist(void);
@@ -47,8 +52,9 @@ static int get_fslist(void);
 static void
 usage(int retcode)
 {
-       fprintf(stderr, "usage: vquota check directory\n");
-       fprintf(stderr, "       vquota lsfs\n");
+       fprintf(stderr, "usage: vquota [-D] check directory\n");
+       fprintf(stderr, "       vquota [-D] lsfs\n");
+       fprintf(stderr, "       vquota [-D] show mount_point\n");
        exit(retcode);
 }
 
@@ -115,20 +121,143 @@ static int get_fslist(void) {
        return 0;
 }
 
+static bool
+send_command(const char *path, const char *cmd,
+               prop_dictionary_t args, prop_dictionary_t *res) {
+       prop_dictionary_t dict;
+       struct plistref pref;
+
+       bool rv;
+       int error;
+
+       dict = prop_dictionary_create();
+
+       if (dict == NULL) {
+               printf("send_command(): couldn't create dictionary\n");
+               return false;
+       }
+
+       rv = prop_dictionary_set_cstring(dict, "command", cmd);
+       if (rv== false) {
+               printf("send_command(): couldn't initialize dictionary\n");
+               return false;
+       }
+
+       rv = prop_dictionary_set(dict, "arguments", args);
+       if (rv == false) {
+               printf("prop_dictionary_set() failed\n");
+               return false;
+       }
+
+       error = prop_dictionary_send_syscall(dict, &pref);
+       if (error != 0) {
+               printf("prop_dictionary_send_syscall() failed\n");
+               prop_object_release(dict);
+               return false;
+       }
+
+       if (flag_debug)
+               printf("message to kernel:\n%s\n", prop_dictionary_externalize(dict));
+
+       error = vquotactl(path, &pref);
+       if (error != 0) {
+               printf("send_command: vquotactl = %d\n", error);
+               return false;
+       }
+
+       error = prop_dictionary_recv_syscall(&pref, res);
+       if (error != 0) {
+               printf("prop_dictionary_recv_syscall() failed\n");
+       }
+
+       if (flag_debug)
+               printf("Message from kernel:\n%s\n", prop_dictionary_externalize(*res));
+
+       return true;
+}
+
+/* show collected statistics on mount point */
+static int show_mp(char *path) {
+       prop_dictionary_t args, res;
+       prop_array_t reslist;
+       bool rv;
+       prop_object_iterator_t  iter;
+       prop_dictionary_t item;
+       uint32_t id;
+       uint64_t space;
+
+       args = prop_dictionary_create();
+       res  = prop_dictionary_create();
+       if (args == NULL)
+               printf("couldn't create args dictionary\n");
+
+       rv = send_command(path, "get usage all", args, &res);
+       if (rv == false) {
+               printf("show-mp(): failed to send message to kernel\n");
+               goto end;
+       }
+
+       reslist = prop_dictionary_get(res, "get usage all");
+       if (reslist == NULL) {
+               printf("show_mp(): failed to get array of results");
+               rv = false;
+               goto end;
+       }
+
+       iter = prop_array_iterator(reslist);
+       if (iter == NULL) {
+               printf("show_mp(): failed to create iterator\n");
+               rv = false;
+               goto end;
+       }
+
+       while ((item = prop_object_iterator_next(iter)) != NULL) {
+               rv = prop_dictionary_get_uint64(item, "space used", &space);
+               if (prop_dictionary_get_uint32(item, "uid", &id))
+                       printf("uid %u:", id);
+               else if (prop_dictionary_get_uint32(item, "gid", &id))
+                       printf("gid %u:", id);
+               else
+                       printf("total space used");
+               printf(" %" PRIu64 "\n", space);
+       }
+       prop_object_iterator_release(iter);
+
+end:
+       prop_object_release(args);
+       prop_object_release(res);
+       return (rv == true);
+}
+
 int
-main(int argc, char **argv)
-{
-       if (argc < 2)
+main(int argc, char **argv) {
+       int ch;
+
+       while ((ch = getopt(argc, argv, "D")) != -1) {
+               switch(ch) {
+               case 'D':
+                       flag_debug = 1;
+                       break;
+               }
+       }
+       argc -= optind;
+       argv += optind;
+       if (argc < 1)
                usage(1);
        
-       if (strcmp(argv[1], "check") == 0) {
-               if (argc < 3)
+       if (strcmp(argv[0], "check") == 0) {
+               if (argc != 2)
                        usage(1);
-               return get_dirsize(argv[2]);
+               return get_dirsize(argv[1]);
        }
-       if (strcmp(argv[1], "lsfs") == 0) {
+       if (strcmp(argv[0], "lsfs") == 0) {
                return get_fslist();
        }
+       if (strcmp(argv[0], "show") == 0) {
+               if (argc != 2)
+                       usage(1);
+               return show_mp(argv[1]);
+       }
 
-       return(0);
+       usage(0);
 }
index dec6480..3c0762b 100644 (file)
@@ -563,4 +563,5 @@ struct sysent sysent[] = {
        { AS(readlinkat_args), (sy_call_t *)sys_readlinkat },   /* 527 = readlinkat */
        { AS(symlinkat_args), (sy_call_t *)sys_symlinkat },     /* 528 = symlinkat */
        { AS(swapoff_args), (sy_call_t *)sys_swapoff }, /* 529 = swapoff */
+       { AS(vquotactl_args), (sy_call_t *)sys_vquotactl },     /* 530 = vquotactl */
 };
index dd463ff..0c8e3f0 100644 (file)
@@ -537,4 +537,5 @@ const char *syscallnames[] = {
        "readlinkat",                   /* 527 = readlinkat */
        "symlinkat",                    /* 528 = symlinkat */
        "swapoff",                      /* 529 = swapoff */
+       "vquotactl",                    /* 530 = vquotactl */
 };
index 5379c02..843f90e 100644 (file)
                                  size_t bufsize); }
 528    STD     POSIX   { int symlinkat(char *path1, int fd, char *path2); }
 529    STD     BSD     { int swapoff(char *name); }
+530    STD     BSD     { int vquotactl(const char *path, \
+                           struct plistref *pref); }
index 8710e54..5273176 100644 (file)
 #include <sys/vfs_quota.h>
 #include <sys/spinlock.h>
 #include <sys/spinlock2.h>
+#include <inttypes.h>
 
-/* in-memory accounting, red-black tree based */
+#include <sys/sysproto.h>
+#include <libprop/proplib.h>
+#include <libprop/prop_dictionary.h>
 
+/* in-memory accounting, red-black tree based */
 /* FIXME: code duplication caused by uid_t / gid_t differences */
 RB_PROTOTYPE(ac_utree, ac_unode, rb_entry, rb_ac_unode_cmp);
 RB_PROTOTYPE(ac_gtree, ac_gnode, rb_entry, rb_ac_gnode_cmp);
@@ -156,3 +160,104 @@ vfs_stdaccount(struct mount *mp, uid_t uid, gid_t gid, int64_t delta)
 
        spin_unlock(&mp->mnt_acct.ac_spin);
 }
+
+static void
+cmd_get_usage_all(struct mount *mp, prop_array_t dict_out) {
+       struct ac_unode *unp;
+       struct ac_gnode *gnp;
+       int i;
+       prop_dictionary_t item;
+
+       item = prop_dictionary_create();
+       (void) prop_dictionary_set_uint64(item, "space used", mp->mnt_acct.ac_bytes);
+       prop_array_add_and_rel(dict_out, item);
+
+       RB_FOREACH(unp, ac_utree, &mp->mnt_acct.ac_uroot) {
+               for (i=0; i<ACCT_CHUNK_NIDS; i++) {
+                       if (unp->uid_chunk[i] != 0) {
+                               item = prop_dictionary_create();
+                               (void) prop_dictionary_set_uint32(item, "uid",
+                                       (unp->left_bits << ACCT_CHUNK_BITS) + i);
+                               (void) prop_dictionary_set_uint64(item, "space used",
+                                       unp->uid_chunk[i]);
+                               prop_array_add_and_rel(dict_out, item);
+                       }
+               }
+       }
+
+       RB_FOREACH(gnp, ac_gtree, &mp->mnt_acct.ac_groot) {
+               for (i=0; i<ACCT_CHUNK_NIDS; i++) {
+                       if (gnp->gid_chunk[i] != 0) {
+                               item = prop_dictionary_create();
+                               (void) prop_dictionary_set_uint32(item, "gid",
+                                       (gnp->left_bits << ACCT_CHUNK_BITS) + i);
+                               (void) prop_dictionary_set_uint64(item, "space used",
+                                       gnp->gid_chunk[i]);
+                               prop_array_add_and_rel(dict_out, item);
+                       }
+               }
+       }
+}
+
+int
+sys_vquotactl(struct vquotactl_args *vqa)
+/* const char *path, struct plistref *pref */
+{
+       const char *path;
+       struct plistref pref;
+       prop_dictionary_t dict, args;
+       char *cmd;
+
+       prop_array_t pa_out;
+
+       struct nlookupdata nd;
+       struct mount *mp;
+       int error;
+
+       path = vqa->path;
+       error = copyin(vqa->pref, &pref, sizeof(pref));
+       error = prop_dictionary_copyin(&pref, &dict);
+       if (error != 0)
+               return(error);
+
+       /* we have a path, get its mount point */
+       error = nlookup_init(&nd, path, UIO_USERSPACE, 0);
+       if (error != 0)
+               return (error);
+       error = nlookup(&nd);
+       if (error != 0)
+               return (error);
+       mp = nd.nl_nch.mount;
+       nlookup_done(&nd);
+
+       /* get the command */
+       if (prop_dictionary_get_cstring(dict, "command", &cmd) == 0) {
+               kprintf("sys_vquotactl(): couldn't get command\n");
+               return EINVAL;
+       }
+       args = prop_dictionary_get(dict, "arguments");
+       if (args == NULL) {
+               kprintf("couldn't get arguments\n");
+               return EINVAL;
+       }
+
+       pa_out = prop_array_create();
+       if (pa_out == NULL)
+               return ENOMEM;
+
+       if (strcmp(cmd, "get usage all") == 0) {
+               cmd_get_usage_all(mp, pa_out);
+               goto done;
+       }
+       return EINVAL;
+
+done:
+       /* kernel to userland */
+       dict = prop_dictionary_create();
+       error = prop_dictionary_set(dict, "get usage all", pa_out);
+
+       error = prop_dictionary_copyout(&pref, dict);
+       error = copyout(&pref, vqa->pref, sizeof(pref));
+
+       return error;
+}
index a59a932..8d1e086 100644 (file)
@@ -360,3 +360,4 @@ HIDE_POSIX(mknodat)
 HIDE_POSIX(readlinkat)
 HIDE_POSIX(symlinkat)
 HIDE_BSD(swapoff)
+HIDE_BSD(vquotactl)
index ca285ed..a97cb28 100644 (file)
 #define        SYS_readlinkat  527
 #define        SYS_symlinkat   528
 #define        SYS_swapoff     529
-#define        SYS_MAXSYSCALL  530
+#define        SYS_vquotactl   530
+#define        SYS_MAXSYSCALL  531
index 4eb0270..5a8f8d8 100644 (file)
@@ -308,4 +308,5 @@ MIASM =  \
        mknodat.o \
        readlinkat.o \
        symlinkat.o \
-       swapoff.o
+       swapoff.o \
+       vquotactl.o
index d2e1b3b..e02fa51 100644 (file)
@@ -2358,6 +2358,13 @@ struct   swapoff_args {
 #endif
        char *  name;   char name_[PAD_(char *)];
 };
+struct vquotactl_args {
+#ifdef _KERNEL
+       struct sysmsg sysmsg;
+#endif
+       const char *    path;   char path_[PAD_(const char *)];
+       struct plistref *       pref;   char pref_[PAD_(struct plistref *)];
+};
 
 #ifdef COMPAT_43
 
@@ -2974,6 +2981,7 @@ int       sys_mknodat (struct mknodat_args *);
 int    sys_readlinkat (struct readlinkat_args *);
 int    sys_symlinkat (struct symlinkat_args *);
 int    sys_swapoff (struct swapoff_args *);
+int    sys_vquotactl (struct vquotactl_args *);
 
 #endif /* !_SYS_SYSPROTO_H_ */
 #undef PAD_
index d120fe6..6006e90 100644 (file)
@@ -416,4 +416,5 @@ union sysunion {
        struct  readlinkat_args readlinkat;
        struct  symlinkat_args symlinkat;
        struct  swapoff_args swapoff;
+       struct  vquotactl_args vquotactl;
 };
index afc86aa..dda49e9 100644 (file)
 #define _SYS_VFSQUOTA_H_
 
 #include <sys/mount.h>
+#include <libprop/proplib.h>
 
 extern void vq_init(struct mount*);
 extern void vq_done(struct mount*);
 
+int vquotactl(const char *path, struct plistref *pref);
+
 #endif