VFS accounting: add a new vquotactl() syscall
[dragonfly.git] / sbin / vquota / vquota.c
1 /*
2  * Copyright (c) 2011 François Tigeot <ftigeot@wolfpond.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in
13  *    the documentation and/or other materials provided with the
14  *    distribution.
15  * 3. Neither the name of The DragonFly Project nor the names of its
16  *    contributors may be used to endorse or promote products derived
17  *    from this software without specific, prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
23  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
25  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
29  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32
33 #include <sys/stat.h>
34 #include <sys/mount.h>
35 #include <sys/vfs_quota.h>
36
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <err.h>
40 #include <string.h>
41 #include <fts.h>
42 #include <libprop/proplib.h>
43 #include <unistd.h>
44 #include <inttypes.h>
45
46 static bool flag_debug = 0;
47
48 static void usage(int);
49 static int get_dirsize(char *);
50 static int get_fslist(void);
51
52 static void
53 usage(int retcode)
54 {
55         fprintf(stderr, "usage: vquota [-D] check directory\n");
56         fprintf(stderr, "       vquota [-D] lsfs\n");
57         fprintf(stderr, "       vquota [-D] show mount_point\n");
58         exit(retcode);
59 }
60
61 static int
62 get_dirsize(char* dirname)
63 {
64         FTS             *fts;
65         FTSENT          *p;
66         char*           fts_args[2];
67         uint64_t        size_of_files = 0;
68         int             retval = 0;
69
70         /* TODO: check directory name sanity */
71         fts_args[0] = dirname;
72         fts_args[1] = NULL;
73
74         if ((fts = fts_open(fts_args, FTS_PHYSICAL, NULL)) == NULL)
75                 err(1, "fts_open() failed");
76
77         while ((p = fts_read(fts)) != NULL) {
78                 switch (p->fts_info) {
79                 /* directories, ignore them */
80                 case FTS_D:
81                 case FTS_DC:
82                 case FTS_DP:
83                         break;
84                 /* read errors, warn, continue and flag */
85                 case FTS_DNR:
86                 case FTS_ERR:
87                 case FTS_NS:
88                         warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
89                         retval = 1;
90                         break;
91                 default:
92                         size_of_files += p->fts_statp->st_size;
93                 }
94         }
95         fts_close(fts);
96
97         printf("%"PRIu64"\n", size_of_files);
98         return retval;
99 }
100
101 /* print a list of filesystems with accounting enabled */
102 static int get_fslist(void) {
103         struct statfs *mntbufp;
104         int nloc, i;
105
106         /* read mount table from kernel */
107         nloc = getmntinfo(&mntbufp, MNT_NOWAIT|MNT_LOCAL);
108         if (nloc <= 0) {
109                 perror("getmntinfo");
110                 exit(1);
111         }
112
113         /* iterate mounted filesystems */
114         for (i=0; i<nloc; i++) {
115             /* vfs accounting enabled on this one ? */
116             if (mntbufp[i].f_flags & MNT_ACCOUNTING)
117                 printf("%s on %s\n", mntbufp[i].f_mntfromname,
118                                                 mntbufp[i].f_mntonname);
119         }
120
121         return 0;
122 }
123
124 static bool
125 send_command(const char *path, const char *cmd,
126                 prop_dictionary_t args, prop_dictionary_t *res) {
127         prop_dictionary_t dict;
128         struct plistref pref;
129
130         bool rv;
131         int error;
132
133         dict = prop_dictionary_create();
134
135         if (dict == NULL) {
136                 printf("send_command(): couldn't create dictionary\n");
137                 return false;
138         }
139
140         rv = prop_dictionary_set_cstring(dict, "command", cmd);
141         if (rv== false) {
142                 printf("send_command(): couldn't initialize dictionary\n");
143                 return false;
144         }
145
146         rv = prop_dictionary_set(dict, "arguments", args);
147         if (rv == false) {
148                 printf("prop_dictionary_set() failed\n");
149                 return false;
150         }
151
152         error = prop_dictionary_send_syscall(dict, &pref);
153         if (error != 0) {
154                 printf("prop_dictionary_send_syscall() failed\n");
155                 prop_object_release(dict);
156                 return false;
157         }
158
159         if (flag_debug)
160                 printf("message to kernel:\n%s\n", prop_dictionary_externalize(dict));
161
162         error = vquotactl(path, &pref);
163         if (error != 0) {
164                 printf("send_command: vquotactl = %d\n", error);
165                 return false;
166         }
167
168         error = prop_dictionary_recv_syscall(&pref, res);
169         if (error != 0) {
170                 printf("prop_dictionary_recv_syscall() failed\n");
171         }
172
173         if (flag_debug)
174                 printf("Message from kernel:\n%s\n", prop_dictionary_externalize(*res));
175
176         return true;
177 }
178
179 /* show collected statistics on mount point */
180 static int show_mp(char *path) {
181         prop_dictionary_t args, res;
182         prop_array_t reslist;
183         bool rv;
184         prop_object_iterator_t  iter;
185         prop_dictionary_t item;
186         uint32_t id;
187         uint64_t space;
188
189         args = prop_dictionary_create();
190         res  = prop_dictionary_create();
191         if (args == NULL)
192                 printf("couldn't create args dictionary\n");
193
194         rv = send_command(path, "get usage all", args, &res);
195         if (rv == false) {
196                 printf("show-mp(): failed to send message to kernel\n");
197                 goto end;
198         }
199
200         reslist = prop_dictionary_get(res, "get usage all");
201         if (reslist == NULL) {
202                 printf("show_mp(): failed to get array of results");
203                 rv = false;
204                 goto end;
205         }
206
207         iter = prop_array_iterator(reslist);
208         if (iter == NULL) {
209                 printf("show_mp(): failed to create iterator\n");
210                 rv = false;
211                 goto end;
212         }
213
214         while ((item = prop_object_iterator_next(iter)) != NULL) {
215                 rv = prop_dictionary_get_uint64(item, "space used", &space);
216                 if (prop_dictionary_get_uint32(item, "uid", &id))
217                         printf("uid %u:", id);
218                 else if (prop_dictionary_get_uint32(item, "gid", &id))
219                         printf("gid %u:", id);
220                 else
221                         printf("total space used");
222                 printf(" %" PRIu64 "\n", space);
223         }
224         prop_object_iterator_release(iter);
225
226 end:
227         prop_object_release(args);
228         prop_object_release(res);
229         return (rv == true);
230 }
231
232 int
233 main(int argc, char **argv) {
234         int ch;
235
236         while ((ch = getopt(argc, argv, "D")) != -1) {
237                 switch(ch) {
238                 case 'D':
239                         flag_debug = 1;
240                         break;
241                 }
242         }
243         argc -= optind;
244         argv += optind;
245         if (argc < 1)
246                 usage(1);
247         
248         if (strcmp(argv[0], "check") == 0) {
249                 if (argc != 2)
250                         usage(1);
251                 return get_dirsize(argv[1]);
252         }
253         if (strcmp(argv[0], "lsfs") == 0) {
254                 return get_fslist();
255         }
256         if (strcmp(argv[0], "show") == 0) {
257                 if (argc != 2)
258                         usage(1);
259                 return show_mp(argv[1]);
260         }
261
262         usage(0);
263 }