]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - quota/report.c
Make xfs_quota reporting match standard tools wrt timestamp rounding.
[thirdparty/xfsprogs-dev.git] / quota / report.c
1 /*
2 * Copyright (c) 2005 Silicon Graphics, Inc.
3 * All Rights Reserved.
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it would be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write the Free Software Foundation,
16 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19 #include <xfs/command.h>
20 #include <pwd.h>
21 #include <grp.h>
22 #include <utmp.h>
23 #include "init.h"
24 #include "quota.h"
25
26 static cmdinfo_t dump_cmd;
27 static cmdinfo_t report_cmd;
28
29 static void
30 dump_help(void)
31 {
32 dump_cmd.args = _("[-gpu] [-f file]");
33 dump_cmd.oneline = _("dump quota information for backup utilities");
34 printf(_(
35 "\n"
36 " create a backup file which contains quota limits information\n"
37 " -g -- dump out group quota limits\n"
38 " -p -- dump out project quota limits\n"
39 " -u -- dump out user quota limits (default)\n"
40 " -f -- write the dump out to the specified file\n"
41 "\n"));
42 }
43
44 static void
45 report_help(void)
46 {
47 report_cmd.args = _("[-bir] [-gpu] [-ahnt] [-f file]");
48 report_cmd.oneline = _("report filesystem quota information");
49 printf(_(
50 "\n"
51 " report used space and inodes, and quota limits, for a filesystem\n"
52 " Example:\n"
53 " 'report -igh'\n"
54 " (reports inode usage for all groups, in an easy-to-read format)\n"
55 " This command is the equivalent of the traditional repquota command, which\n"
56 " prints a summary of the disk usage and quotas for the current filesystem,\n"
57 " or all filesystems.\n"
58 " -a -- report for all mounted filesystems with quota enabled\n"
59 " -h -- report in a human-readable format\n"
60 " -n -- skip identifier-to-name translations, just report IDs\n"
61 " -N -- suppress the header from the output\n"
62 " -t -- terse output format, hides rows which are all zero\n"
63 " -g -- report group usage and quota information\n"
64 " -p -- report project usage and quota information\n"
65 " -u -- report user usage and quota information\n"
66 " -b -- report blocks-used information only\n"
67 " -i -- report inodes-used information only\n"
68 " -r -- report realtime-blocks-used information only\n"
69 "\n"));
70 }
71
72 static void
73 dump_file(
74 FILE *fp,
75 uint id,
76 uint type,
77 char *dev)
78 {
79 fs_disk_quota_t d;
80
81 if (xfsquotactl(XFS_GETQUOTA, dev, type, id, (void *)&d) < 0)
82 return;
83 if (!d.d_blk_softlimit && !d.d_blk_hardlimit &&
84 !d.d_ino_softlimit && !d.d_ino_hardlimit &&
85 !d.d_rtb_softlimit && !d.d_rtb_hardlimit)
86 return;
87 fprintf(fp, "fs = %s\n", dev);
88 /* this branch is for backward compatibility reasons */
89 if (d.d_rtb_softlimit || d.d_rtb_hardlimit)
90 fprintf(fp, "%-10d %7llu %7llu %7llu %7llu %7llu %7llu\n", id,
91 (unsigned long long)d.d_blk_softlimit,
92 (unsigned long long)d.d_blk_hardlimit,
93 (unsigned long long)d.d_ino_softlimit,
94 (unsigned long long)d.d_ino_hardlimit,
95 (unsigned long long)d.d_rtb_softlimit,
96 (unsigned long long)d.d_rtb_hardlimit);
97 else
98 fprintf(fp, "%-10d %7llu %7llu %7llu %7llu\n", id,
99 (unsigned long long)d.d_blk_softlimit,
100 (unsigned long long)d.d_blk_hardlimit,
101 (unsigned long long)d.d_ino_softlimit,
102 (unsigned long long)d.d_ino_hardlimit);
103 }
104
105 static void
106 dump_limits_any_type(
107 FILE *fp,
108 uint type,
109 char *dir,
110 uint lower,
111 uint upper)
112 {
113 fs_path_t *mount;
114 uint id;
115
116 if ((mount = fs_table_lookup(dir, FS_MOUNT_POINT)) == NULL) {
117 fprintf(stderr, "%s: cannot find mount point %s\n",
118 progname, dir);
119 return;
120 }
121
122 if (upper) {
123 for (id = lower; id < upper; id++)
124 dump_file(fp, id, type, mount->fs_name);
125 return;
126 }
127
128 switch (type) {
129 case XFS_GROUP_QUOTA: {
130 struct group *g;
131 setgrent();
132 while ((g = getgrent()) != NULL)
133 dump_file(fp, g->gr_gid, type, mount->fs_name);
134 endgrent();
135 break;
136 }
137 case XFS_PROJ_QUOTA: {
138 struct fs_project *p;
139 setprent();
140 while ((p = getprent()) != NULL)
141 dump_file(fp, p->pr_prid, type, mount->fs_name);
142 endprent();
143 break;
144 }
145 case XFS_USER_QUOTA: {
146 struct passwd *u;
147 setpwent();
148 while ((u = getpwent()) != NULL)
149 dump_file(fp, u->pw_uid, type, mount->fs_name);
150 endpwent();
151 break;
152 }
153 }
154 }
155
156 static int
157 dump_f(
158 int argc,
159 char **argv)
160 {
161 FILE *fp;
162 char *fname = NULL;
163 uint lower = 0, upper = 0;
164 int c, type = XFS_USER_QUOTA;
165
166 while ((c = getopt(argc, argv, "f:gpuL:U:")) != EOF) {
167 switch(c) {
168 case 'f':
169 fname = optarg;
170 break;
171 case 'g':
172 type = XFS_GROUP_QUOTA;
173 break;
174 case 'p':
175 type = XFS_PROJ_QUOTA;
176 break;
177 case 'u':
178 type = XFS_USER_QUOTA;
179 break;
180 case 'L':
181 lower = (uint)atoi(optarg);
182 break;
183 case 'U':
184 upper = (uint)atoi(optarg);
185 break;
186 default:
187 return command_usage(&dump_cmd);
188 }
189 }
190
191 if (argc != optind)
192 return command_usage(&dump_cmd);
193
194 if ((fp = fopen_write_secure(fname)) == NULL)
195 return 0;
196
197 dump_limits_any_type(fp, type, fs_path->fs_dir, lower, upper);
198
199 if (fname)
200 fclose(fp);
201
202 return 0;
203 }
204
205 static void
206 report_header(
207 FILE *fp,
208 uint form,
209 uint type,
210 fs_path_t *mount,
211 int flags)
212 {
213 char *typename = type_to_string(type);
214 char scratch[64];
215 uint i, count;
216
217 if (flags & NO_HEADER_FLAG)
218 return;
219
220 /* line 1 */
221 fprintf(fp, _("%s quota on %s (%s)\n"),
222 typename, mount->fs_dir, mount->fs_name);
223
224 /* line 2 */
225 for (i = 0; i < 10; i++)
226 fputc(' ', fp);
227 if (form & XFS_BLOCK_QUOTA)
228 fprintf(fp, (flags & HUMAN_FLAG) ?
229 "%13c %s %13c" : "%20c %s %20c",
230 ' ', form_to_string(XFS_BLOCK_QUOTA), ' ');
231 if (form & XFS_INODE_QUOTA)
232 fprintf(fp, (flags & HUMAN_FLAG) ?
233 "%13c %s %13c" : "%20c %s %20c",
234 ' ', form_to_string(XFS_INODE_QUOTA), ' ');
235 if (form & XFS_RTBLOCK_QUOTA)
236 fprintf(fp, (flags & HUMAN_FLAG) ?
237 "%9c %s %9c" : "%15c %s %15c",
238 ' ', form_to_string(XFS_RTBLOCK_QUOTA), ' ');
239 fputc('\n', fp);
240
241 /* line 3 */
242 snprintf(scratch, sizeof(scratch), "%s ID", typename);
243 fprintf(fp, "%-10s ", scratch);
244 if (form & XFS_BLOCK_QUOTA)
245 fprintf(fp, (flags & HUMAN_FLAG) ?
246 _(" Used Soft Hard Warn/Grace ") :
247 _(" Used Soft Hard Warn/Grace "));
248 if (form & XFS_INODE_QUOTA)
249 fprintf(fp, (flags & HUMAN_FLAG) ?
250 _(" Used Soft Hard Warn/Grace ") :
251 _(" Used Soft Hard Warn/ Grace "));
252 if (form & XFS_RTBLOCK_QUOTA)
253 fprintf(fp, (flags & HUMAN_FLAG) ?
254 _(" Used Soft Hard Warn/Grace ") :
255 _(" Used Soft Hard Warn/Grace "));
256 fputc('\n', fp);
257
258 /* line 4 */
259 for (i = 0; i < 10; i++)
260 fputc('-', fp);
261 fputc(' ', fp);
262 count = (flags & HUMAN_FLAG) ? 33 : 50;
263 if (form & XFS_BLOCK_QUOTA) {
264 for (i = 0; i < count; i++)
265 fputc('-', fp);
266 fputc(' ', fp);
267 }
268 if (form & XFS_INODE_QUOTA) {
269 for (i = 0; i < count; i++)
270 fputc('-', fp);
271 fputc(' ', fp);
272 }
273 if (form & XFS_RTBLOCK_QUOTA) {
274 for (i = 0; i < count; i++)
275 fputc('-', fp);
276 fputc(' ', fp);
277 }
278 fputc('\n', fp);
279 }
280
281 static int
282 report_mount(
283 FILE *fp,
284 __uint32_t id,
285 char *name,
286 uint form,
287 uint type,
288 fs_path_t *mount,
289 uint flags)
290 {
291 fs_disk_quota_t d;
292 char *dev = mount->fs_name;
293 char c[8], h[8], s[8];
294 uint qflags;
295 int count;
296
297 if (xfsquotactl(XFS_GETQUOTA, dev, type, id, (void *)&d) < 0)
298 return 0;
299
300 if (flags & TERSE_FLAG) {
301 count = 0;
302 if ((form & XFS_BLOCK_QUOTA) && d.d_bcount)
303 count++;
304 if ((form & XFS_INODE_QUOTA) && d.d_icount)
305 count++;
306 if ((form & XFS_RTBLOCK_QUOTA) && d.d_rtbcount)
307 count++;
308 if (!count)
309 return 0;
310 }
311
312 if (!(flags & NO_HEADER_FLAG))
313 report_header(fp, form, type, mount, flags);
314
315 fprintf(fp, "%-10s", name);
316 if (form & XFS_BLOCK_QUOTA) {
317 qflags = (flags & HUMAN_FLAG);
318 if (d.d_blk_hardlimit && d.d_bcount > d.d_blk_hardlimit)
319 qflags |= LIMIT_FLAG;
320 if (d.d_blk_softlimit && d.d_bcount > d.d_blk_softlimit)
321 qflags |= QUOTA_FLAG;
322 if (flags & HUMAN_FLAG)
323 fprintf(fp, " %6s %6s %6s %02d %8s",
324 bbs_to_string(d.d_bcount, c, sizeof(c)),
325 bbs_to_string(d.d_blk_softlimit, s, sizeof(s)),
326 bbs_to_string(d.d_blk_hardlimit, h, sizeof(h)),
327 d.d_bwarns,
328 time_to_string(d.d_btimer, qflags));
329 else
330 fprintf(fp, " %10llu %10llu %10llu %02d %9s",
331 (unsigned long long)d.d_bcount >> 1,
332 (unsigned long long)d.d_blk_softlimit >> 1,
333 (unsigned long long)d.d_blk_hardlimit >> 1,
334 d.d_bwarns,
335 time_to_string(d.d_btimer, qflags));
336 }
337 if (form & XFS_INODE_QUOTA) {
338 qflags = (flags & HUMAN_FLAG);
339 if (d.d_ino_hardlimit && d.d_icount > d.d_ino_hardlimit)
340 qflags |= LIMIT_FLAG;
341 if (d.d_ino_softlimit && d.d_icount > d.d_ino_softlimit)
342 qflags |= QUOTA_FLAG;
343 if (flags & HUMAN_FLAG)
344 fprintf(fp, " %6s %6s %6s %02d %8s",
345 num_to_string(d.d_icount, c, sizeof(c)),
346 num_to_string(d.d_ino_softlimit, s, sizeof(s)),
347 num_to_string(d.d_ino_hardlimit, h, sizeof(h)),
348 d.d_iwarns,
349 time_to_string(d.d_itimer, qflags));
350 else
351 fprintf(fp, " %10llu %10llu %10llu %02d %9s",
352 (unsigned long long)d.d_icount,
353 (unsigned long long)d.d_ino_softlimit,
354 (unsigned long long)d.d_ino_hardlimit,
355 d.d_iwarns,
356 time_to_string(d.d_itimer, qflags));
357 }
358 if (form & XFS_RTBLOCK_QUOTA) {
359 qflags = (flags & HUMAN_FLAG);
360 if (d.d_rtb_hardlimit && d.d_rtbcount > d.d_rtb_hardlimit)
361 qflags |= LIMIT_FLAG;
362 if (d.d_rtb_softlimit && d.d_rtbcount > d.d_rtb_softlimit)
363 qflags |= QUOTA_FLAG;
364 if (flags & HUMAN_FLAG)
365 fprintf(fp, " %6s %6s %6s %02d %8s",
366 bbs_to_string(d.d_rtbcount, c, sizeof(c)),
367 bbs_to_string(d.d_rtb_softlimit, s, sizeof(s)),
368 bbs_to_string(d.d_rtb_hardlimit, h, sizeof(h)),
369 d.d_rtbwarns,
370 time_to_string(d.d_rtbtimer, qflags));
371 else
372 fprintf(fp, " %10llu %10llu %10llu %02d %9s",
373 (unsigned long long)d.d_rtbcount >> 1,
374 (unsigned long long)d.d_rtb_softlimit >> 1,
375 (unsigned long long)d.d_rtb_hardlimit >> 1,
376 d.d_rtbwarns,
377 time_to_string(d.d_rtbtimer, qflags));
378 }
379 fputc('\n', fp);
380 return 1;
381 }
382
383 static void
384 report_user_mount(
385 FILE *fp,
386 uint form,
387 fs_path_t *mount,
388 uint lower,
389 uint upper,
390 uint flags)
391 {
392 struct passwd *u;
393 char n[NMAX];
394 uint id;
395
396 if (upper) { /* identifier range specified */
397 for (id = lower; id < upper; id++) {
398 snprintf(n, sizeof(n)-1, "#%u", id);
399 if (report_mount(fp, id, n,
400 form, XFS_USER_QUOTA, mount, flags))
401 flags |= NO_HEADER_FLAG;
402 }
403 } else {
404 setpwent();
405 while ((u = getpwent()) != NULL) {
406 if (flags & NO_LOOKUP_FLAG)
407 snprintf(n, sizeof(n)-1, "#%u", u->pw_uid);
408 else
409 strncpy(n, u->pw_name, sizeof(n)-1);
410 if (report_mount(fp, u->pw_uid, n,
411 form, XFS_USER_QUOTA, mount, flags))
412 flags |= NO_HEADER_FLAG;
413 }
414 endpwent();
415 }
416
417 if (flags & NO_HEADER_FLAG)
418 fputc('\n', fp);
419 }
420
421 static void
422 report_group_mount(
423 FILE *fp,
424 uint form,
425 fs_path_t *mount,
426 uint lower,
427 uint upper,
428 uint flags)
429 {
430 struct group *g;
431 char n[NMAX];
432 uint id;
433
434 if (upper) { /* identifier range specified */
435 for (id = lower; id < upper; id++) {
436 snprintf(n, sizeof(n)-1, "#%u", id);
437 if (report_mount(fp, id, n,
438 form, XFS_GROUP_QUOTA, mount, flags))
439 flags |= NO_HEADER_FLAG;
440 }
441 } else {
442 setgrent();
443 while ((g = getgrent()) != NULL) {
444 if (flags & NO_LOOKUP_FLAG)
445 snprintf(n, sizeof(n)-1, "#%u", g->gr_gid);
446 else
447 strncpy(n, g->gr_name, sizeof(n)-1);
448 if (report_mount(fp, g->gr_gid, n,
449 form, XFS_GROUP_QUOTA, mount, flags))
450 flags |= NO_HEADER_FLAG;
451 }
452 }
453 if (flags & NO_HEADER_FLAG)
454 fputc('\n', fp);
455 endgrent();
456 }
457
458 static void
459 report_project_mount(
460 FILE *fp,
461 uint form,
462 fs_path_t *mount,
463 uint lower,
464 uint upper,
465 uint flags)
466 {
467 fs_project_t *p;
468 char n[NMAX];
469 uint id;
470
471 if (upper) { /* identifier range specified */
472 for (id = lower; id < upper; id++) {
473 snprintf(n, sizeof(n)-1, "#%u", id);
474 if (report_mount(fp, id, n,
475 form, XFS_PROJ_QUOTA, mount, flags))
476 flags |= NO_HEADER_FLAG;
477 }
478 } else {
479 setprent();
480 while ((p = getprent()) != NULL) {
481 if (flags & NO_LOOKUP_FLAG)
482 snprintf(n, sizeof(n)-1, "#%u",
483 (unsigned int)p->pr_prid);
484 else
485 strncpy(n, p->pr_name, sizeof(n)-1);
486 if (report_mount(fp, p->pr_prid, n,
487 form, XFS_PROJ_QUOTA, mount, flags))
488 flags |= NO_HEADER_FLAG;
489 }
490 endprent();
491 }
492
493 if (flags & NO_HEADER_FLAG)
494 fputc('\n', fp);
495 }
496
497 static void
498 report_any_type(
499 FILE *fp,
500 uint form,
501 uint type,
502 char *dir,
503 uint lower,
504 uint upper,
505 uint flags)
506 {
507 fs_cursor_t cursor;
508 fs_path_t *mount;
509
510 if (type & XFS_USER_QUOTA) {
511 fs_cursor_initialise(dir, FS_MOUNT_POINT, &cursor);
512 while ((mount = fs_cursor_next_entry(&cursor))) {
513 xfsquotactl(XFS_QSYNC, mount->fs_name,
514 XFS_USER_QUOTA, 0, NULL);
515 report_user_mount(fp, form, mount,
516 lower, upper, flags);
517 }
518 }
519 if (type & XFS_GROUP_QUOTA) {
520 fs_cursor_initialise(dir, FS_MOUNT_POINT, &cursor);
521 while ((mount = fs_cursor_next_entry(&cursor))) {
522 xfsquotactl(XFS_QSYNC, mount->fs_name,
523 XFS_GROUP_QUOTA, 0, NULL);
524 report_group_mount(fp, form, mount,
525 lower, upper, flags);
526 }
527 }
528 if (type & XFS_PROJ_QUOTA) {
529 fs_cursor_initialise(dir, FS_MOUNT_POINT, &cursor);
530 while ((mount = fs_cursor_next_entry(&cursor))) {
531 xfsquotactl(XFS_QSYNC, mount->fs_name,
532 XFS_PROJ_QUOTA, 0, NULL);
533 report_project_mount(fp, form, mount,
534 lower, upper, flags);
535 }
536 }
537 }
538
539 static int
540 report_f(
541 int argc,
542 char **argv)
543 {
544 FILE *fp = NULL;
545 char *fname = NULL;
546 uint lower = 0, upper = 0;
547 int c, flags = 0, type = 0, form = 0;
548
549 while ((c = getopt(argc, argv, "abf:ghiL:NnprtuU:")) != EOF) {
550 switch (c) {
551 case 'f':
552 fname = optarg;
553 break;
554 case 'b':
555 form |= XFS_BLOCK_QUOTA;
556 break;
557 case 'i':
558 form |= XFS_INODE_QUOTA;
559 break;
560 case 'r':
561 form |= XFS_RTBLOCK_QUOTA;
562 break;
563 case 'g':
564 type |= XFS_GROUP_QUOTA;
565 break;
566 case 'p':
567 type |= XFS_PROJ_QUOTA;
568 break;
569 case 'u':
570 type |= XFS_USER_QUOTA;
571 break;
572 case 'a':
573 flags |= ALL_MOUNTS_FLAG;
574 break;
575 case 'h':
576 flags |= HUMAN_FLAG;
577 break;
578 case 'n':
579 flags |= NO_LOOKUP_FLAG;
580 break;
581 case 'N':
582 flags |= NO_HEADER_FLAG;
583 break;
584 case 't':
585 flags |= TERSE_FLAG;
586 break;
587 case 'L':
588 lower = (uint)atoi(optarg);
589 break;
590 case 'U':
591 upper = (uint)atoi(optarg);
592 break;
593 default:
594 return command_usage(&report_cmd);
595 }
596 }
597
598 if (!form)
599 form = XFS_BLOCK_QUOTA;
600
601 if (!type)
602 type = XFS_USER_QUOTA | XFS_GROUP_QUOTA | XFS_PROJ_QUOTA;
603
604 if ((fp = fopen_write_secure(fname)) == NULL)
605 return 0;
606
607 if (argc == optind) {
608 if (flags & ALL_MOUNTS_FLAG)
609 report_any_type(fp, form, type, NULL,
610 lower, upper, flags);
611 else if (fs_path->fs_flags & FS_MOUNT_POINT)
612 report_any_type(fp, form, type, fs_path->fs_dir,
613 lower, upper, flags);
614 } else while (argc > optind) {
615 report_any_type(fp, form, type, argv[optind++],
616 lower, upper, flags);
617 }
618
619 if (fname)
620 fclose(fp);
621 return 0;
622 }
623
624 void
625 report_init(void)
626 {
627 dump_cmd.name = _("dump");
628 dump_cmd.cfunc = dump_f;
629 dump_cmd.argmin = 0;
630 dump_cmd.argmax = -1;
631 dump_cmd.args = _("[-gpu] [-f file]");
632 dump_cmd.oneline = _("dump quota information for backup utilities");
633 dump_cmd.help = dump_help;
634
635 report_cmd.name = _("report");
636 report_cmd.altname = _("repquota");
637 report_cmd.cfunc = report_f;
638 report_cmd.argmin = 0;
639 report_cmd.argmax = -1;
640 report_cmd.args = _("[-bir] [-gpu] [-ahnt] [-f file]");
641 report_cmd.oneline = _("report filesystem quota information");
642 report_cmd.help = report_help;
643
644 if (expert) {
645 add_command(&dump_cmd);
646 add_command(&report_cmd);
647 }
648 }