3 * Utility for reporting quotas
5 * Based on old repquota.
6 * Jan Kara <jack@suse.cz> - Sponsored by SuSE CZ
27 #define PRINTNAMELEN 9 /* Number of characters to be reserved for name on screen */
28 #define MAX_CACHE_DQUOTS 1024 /* Number of dquots in cache */
33 #define FL_ALL 8 /* Dump quota files on all filesystems */
34 #define FL_TRUNCNAMES 16 /* Truncate names to fit into the screen */
35 #define FL_SHORTNUMS 32 /* Try to print space in appropriate units */
36 #define FL_NONAME 64 /* Don't translate ids to names */
37 #define FL_NOCACHE 128 /* Don't cache dquots before resolving */
38 #define FL_NOAUTOFS 256 /* Ignore autofs mountpoints */
39 #define FL_RAWGRACE 512 /* Print grace times in seconds since epoch */
45 struct dquot dquot_cache[MAX_CACHE_DQUOTS];
48 static void usage(void)
50 errstr(_("Utility for reporting quotas.\nUsage:\n%s [-vugsi] [-c|C] [-t|n] [-F quotaformat] (-a | mntpoint)\n\n\
51 -v, --verbose display also users/groups without any usage\n\
52 -u, --user display information about users\n\
53 -g, --group display information about groups\n\
54 -s, --human-readable show numbers in human friendly units (MB, GB, ...)\n\
55 -t, --truncate-names truncate names to 8 characters\n\
56 -p, --raw-grace print grace time in seconds since epoch\n\
57 -n, --no-names do not translate uid/gid to name\n\
58 -i, --no-autofs avoid autofs mountpoints\n\
59 -c, --batch-translation translate big number of ids at once\n\
60 -C, --no-batch-translation translate ids one by one\n\
61 -F, --format=formatname report information for specific format\n\
62 -h, --help display this help message and exit\n\
63 -V, --version display version information and exit\n\n"), progname);
64 fprintf(stderr, _("Bugs to %s\n"), MY_EMAIL);
68 static void parse_options(int argcnt, char **argstr)
71 int cache_specified = 0;
72 struct option long_opts[] = {
73 { "version", 0, NULL, 'V' },
74 { "all", 0, NULL, 'a' },
75 { "verbose", 0, NULL, 'v' },
76 { "user", 0, NULL, 'u' },
77 { "group", 0, NULL, 'g' },
78 { "help", 0, NULL, 'h' },
79 { "truncate-names", 0, NULL, 't' },
80 { "raw-grace", 0, NULL, 'p' },
81 { "human-readable", 0, NULL, 's' },
82 { "no-names", 0, NULL, 'n' },
83 { "cache", 0, NULL, 'c' },
84 { "no-cache", 0, NULL, 'C' },
85 { "no-autofs", 0, NULL, 'i' },
86 { "format", 1, NULL, 'F' },
90 while ((ret = getopt_long(argcnt, argstr, "VavughtspncCiF:", long_opts, NULL)) != -1) {
111 flags |= FL_TRUNCNAMES;
114 flags |= FL_RAWGRACE;
117 flags |= FL_SHORTNUMS;
127 flags |= FL_NOAUTOFS;
130 if ((fmt = name2fmt(optarg)) == QF_ERROR)
140 if ((flags & FL_ALL && optind != argcnt) || (!(flags & FL_ALL) && optind == argcnt)) {
141 fputs(_("Bad number of arguments.\n"), stderr);
145 fputs(_("Repquota cannot report through RPC calls.\n"), stderr);
148 if (flags & FL_NONAME && flags & FL_TRUNCNAMES) {
149 fputs(_("Specified both -n and -t but only one of them can be used.\n"), stderr);
152 if (!(flags & (FL_USER | FL_GROUP)))
154 if (!(flags & FL_ALL)) {
155 mnt = argstr + optind;
156 mntcnt = argcnt - optind;
158 if (!cache_specified && !(flags & FL_NONAME) && passwd_handling() == PASSWD_DB)
162 /* Are we over soft or hard limit? */
163 static char overlim(qsize_t usage, qsize_t softlim, qsize_t hardlim)
165 if ((usage > softlim && softlim) || (usage > hardlim && hardlim))
170 /* Print one quota entry */
171 static void print(struct dquot *dquot, char *name)
173 char pname[MAXNAMELEN];
174 char time[MAXTIMELEN];
175 char numbuf[3][MAXNUMLEN];
177 struct util_dqblk *entry = &dquot->dq_dqb;
179 if (!entry->dqb_curspace && !entry->dqb_curinodes && !(flags & FL_VERBOSE))
181 sstrncpy(pname, name, sizeof(pname));
182 if (flags & FL_TRUNCNAMES)
183 pname[PRINTNAMELEN] = 0;
184 if (entry->dqb_bsoftlimit && toqb(entry->dqb_curspace) >= entry->dqb_bsoftlimit)
185 if (flags & FL_RAWGRACE)
186 sprintf(time, "%llu", (unsigned long long)entry->dqb_btime);
188 difftime2str(entry->dqb_btime, time);
190 if (flags & FL_RAWGRACE)
194 space2str(toqb(entry->dqb_curspace), numbuf[0], flags & FL_SHORTNUMS);
195 space2str(entry->dqb_bsoftlimit, numbuf[1], flags & FL_SHORTNUMS);
196 space2str(entry->dqb_bhardlimit, numbuf[2], flags & FL_SHORTNUMS);
197 printf("%-*s %c%c %7s %7s %7s %6s", PRINTNAMELEN, pname,
198 overlim(qb2kb(toqb(entry->dqb_curspace)), qb2kb(entry->dqb_bsoftlimit), qb2kb(entry->dqb_bhardlimit)),
199 overlim(entry->dqb_curinodes, entry->dqb_isoftlimit, entry->dqb_ihardlimit),
200 numbuf[0], numbuf[1], numbuf[2], time);
201 if (entry->dqb_isoftlimit && entry->dqb_curinodes >= entry->dqb_isoftlimit)
202 if (flags & FL_RAWGRACE)
203 sprintf(time, "%llu", (unsigned long long)entry->dqb_itime);
205 difftime2str(entry->dqb_itime, time);
207 if (flags & FL_RAWGRACE)
211 number2str(entry->dqb_curinodes, numbuf[0], flags & FL_SHORTNUMS);
212 number2str(entry->dqb_isoftlimit, numbuf[1], flags & FL_SHORTNUMS);
213 number2str(entry->dqb_ihardlimit, numbuf[2], flags & FL_SHORTNUMS);
214 printf(" %7s %5s %5s %6s\n", numbuf[0], numbuf[1], numbuf[2], time);
217 /* Print all dquots in the cache */
218 static void dump_cached_dquots(int type)
221 char namebuf[MAXNAMELEN];
225 if (type == USRQUOTA) {
226 struct passwd *pwent;
229 while ((pwent = getpwent())) {
230 for (i = 0; i < cached_dquots && pwent->pw_uid != dquot_cache[i].dq_id; i++);
231 if (i < cached_dquots && !(dquot_cache[i].dq_flags & DQ_PRINTED)) {
232 print(dquot_cache+i, pwent->pw_name);
233 dquot_cache[i].dq_flags |= DQ_PRINTED;
242 while ((grent = getgrent())) {
243 for (i = 0; i < cached_dquots && grent->gr_gid != dquot_cache[i].dq_id; i++);
244 if (i < cached_dquots && !(dquot_cache[i].dq_flags & DQ_PRINTED)) {
245 print(dquot_cache+i, grent->gr_name);
246 dquot_cache[i].dq_flags |= DQ_PRINTED;
251 for (i = 0; i < cached_dquots; i++)
252 if (!(dquot_cache[i].dq_flags & DQ_PRINTED)) {
253 sprintf(namebuf, "#%u", dquot_cache[i].dq_id);
254 print(dquot_cache+i, namebuf);
259 /* Callback routine called by scan_dquots on each dquot */
260 static int output(struct dquot *dquot, char *name)
262 if (flags & FL_NONAME) { /* We should translate names? */
263 char namebuf[MAXNAMELEN];
265 sprintf(namebuf, "#%u", dquot->dq_id);
266 print(dquot, namebuf);
268 else if (name || flags & FL_NOCACHE) { /* We shouldn't do batched id->name translations? */
269 char namebuf[MAXNAMELEN];
272 id2name(dquot->dq_id, dquot->dq_h->qh_type, namebuf);
277 else { /* Lets cache the dquot for later printing */
278 memcpy(dquot_cache+cached_dquots++, dquot, sizeof(struct dquot));
279 if (cached_dquots >= MAX_CACHE_DQUOTS)
280 dump_cached_dquots(dquot->dq_h->qh_type);
285 /* Dump information stored in one quota file */
286 static void report_it(struct quota_handle *h, int type)
288 char bgbuf[MAXTIMELEN], igbuf[MAXTIMELEN];
291 if (flags & FL_SHORTNUMS)
292 spacehdr = _("Space");
294 spacehdr = _("Block");
296 printf(_("*** Report for %s quotas on device %s\n"), type2name(type), h->qh_quotadev);
297 time2str(h->qh_info.dqi_bgrace, bgbuf, TF_ROUND);
298 time2str(h->qh_info.dqi_igrace, igbuf, TF_ROUND);
299 printf(_("Block grace time: %s; Inode grace time: %s\n"), bgbuf, igbuf);
300 printf(_(" %s limits File limits\n"), spacehdr);
301 printf(_("%-9s used soft hard grace used soft hard grace\n"), (type == USRQUOTA)?_("User"):_("Group"));
302 printf("----------------------------------------------------------------------\n");
304 if (h->qh_ops->scan_dquots(h, output) < 0)
306 dump_cached_dquots(type);
307 if (h->qh_ops->report) {
309 h->qh_ops->report(h, flags & FL_VERBOSE);
314 static void report(int type)
316 struct quota_handle **handles;
320 handles = create_handle_list(0, NULL, type, fmt, IOI_READONLY | IOI_OPENFILE, MS_LOCALONLY | (flags & FL_NOAUTOFS ? MS_NO_AUTOFS : 0));
322 handles = create_handle_list(mntcnt, mnt, type, fmt, IOI_READONLY | IOI_OPENFILE, MS_LOCALONLY | (flags & FL_NOAUTOFS ? MS_NO_AUTOFS : 0));
323 for (i = 0; handles[i]; i++)
324 report_it(handles[i], type);
325 dispose_handle_list(handles);
328 int main(int argc, char **argv)
331 progname = basename(argv[0]);
333 parse_options(argc, argv);
334 init_kernel_interface();
339 if (flags & FL_GROUP)