Initial commit to Gerrit
[profile/ivi/quota.git] / repquota.c
1 /*
2  *
3  *      Utility for reporting quotas
4  *
5  *      Based on old repquota.
6  *      Jan Kara <jack@suse.cz> - Sponsored by SuSE CZ
7  */
8
9 #include "config.h"
10
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <fcntl.h>
15 #include <string.h>
16 #include <time.h>
17 #include <errno.h>
18 #include <pwd.h>
19 #include <grp.h>
20 #include <getopt.h>
21
22 #include "pot.h"
23 #include "common.h"
24 #include "quotasys.h"
25 #include "quotaio.h"
26
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 */
29
30 #define FL_USER 1
31 #define FL_GROUP 2
32 #define FL_VERBOSE 4
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 */
40
41 int flags, fmt = -1;
42 char **mnt;
43 int mntcnt;
44 int cached_dquots;
45 struct dquot dquot_cache[MAX_CACHE_DQUOTS];
46 char *progname;
47
48 static void usage(void)
49 {
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);
65         exit(1);
66 }
67
68 static void parse_options(int argcnt, char **argstr)
69 {
70         int ret;
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' },
87                 { NULL, 0, NULL, 0 }
88         };
89
90         while ((ret = getopt_long(argcnt, argstr, "VavughtspncCiF:", long_opts, NULL)) != -1) {
91                 switch (ret) {
92                         case '?':
93                         case 'h':
94                                 usage();
95                         case 'V':
96                                 version();
97                                 exit(0);
98                         case 'u':
99                                 flags |= FL_USER;
100                                 break;
101                         case 'g':
102                                 flags |= FL_GROUP;
103                                 break;
104                         case 'v':
105                                 flags |= FL_VERBOSE;
106                                 break;
107                         case 'a':
108                                 flags |= FL_ALL;
109                                 break;
110                         case 't':
111                                 flags |= FL_TRUNCNAMES;
112                                 break;
113                         case 'p':
114                                 flags |= FL_RAWGRACE;
115                                 break;
116                         case 's':
117                                 flags |= FL_SHORTNUMS;
118                                 break;
119                         case 'C':
120                                 flags |= FL_NOCACHE;
121                                 cache_specified = 1;
122                                 break;
123                         case 'c':
124                                 cache_specified = 1;
125                                 break;
126                         case 'i':
127                                 flags |= FL_NOAUTOFS;
128                                 break;
129                         case 'F':
130                                 if ((fmt = name2fmt(optarg)) == QF_ERROR)
131                                         exit(1);
132                                 break;
133                         case 'n':
134                                 flags |= FL_NONAME;
135                                 break;
136
137                 }
138         }
139
140         if ((flags & FL_ALL && optind != argcnt) || (!(flags & FL_ALL) && optind == argcnt)) {
141                 fputs(_("Bad number of arguments.\n"), stderr);
142                 usage();
143         }
144         if (fmt == QF_RPC) {
145                 fputs(_("Repquota cannot report through RPC calls.\n"), stderr);
146                 exit(1);
147         }
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);
150                 exit(1);
151         }
152         if (!(flags & (FL_USER | FL_GROUP)))
153                 flags |= FL_USER;
154         if (!(flags & FL_ALL)) {
155                 mnt = argstr + optind;
156                 mntcnt = argcnt - optind;
157         }
158         if (!cache_specified && !(flags & FL_NONAME) && passwd_handling() == PASSWD_DB)
159                 flags |= FL_NOCACHE;
160 }
161
162 /* Are we over soft or hard limit? */
163 static char overlim(qsize_t usage, qsize_t softlim, qsize_t hardlim)
164 {
165         if ((usage > softlim && softlim) || (usage > hardlim && hardlim))
166                 return '+';
167         return '-';
168 }
169
170 /* Print one quota entry */
171 static void print(struct dquot *dquot, char *name)
172 {
173         char pname[MAXNAMELEN];
174         char time[MAXTIMELEN];
175         char numbuf[3][MAXNUMLEN];
176         
177         struct util_dqblk *entry = &dquot->dq_dqb;
178
179         if (!entry->dqb_curspace && !entry->dqb_curinodes && !(flags & FL_VERBOSE))
180                 return;
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);
187                 else
188                         difftime2str(entry->dqb_btime, time);
189         else
190                 if (flags & FL_RAWGRACE)
191                         strcpy(time, "0");
192                 else
193                         time[0] = 0;
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);
204                 else
205                         difftime2str(entry->dqb_itime, time);
206         else
207                 if (flags & FL_RAWGRACE)
208                         strcpy(time, "0");
209                 else
210                         time[0] = 0;
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);
215 }
216
217 /* Print all dquots in the cache */
218 static void dump_cached_dquots(int type)
219 {
220         int i;
221         char namebuf[MAXNAMELEN];
222
223         if (!cached_dquots)
224                 return;
225         if (type == USRQUOTA) {
226                 struct passwd *pwent;
227
228                 setpwent();
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;
234                         }
235                 }
236                 endpwent();
237         }
238         else {
239                 struct group *grent;
240
241                 setgrent();
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;
247                         }
248                 }
249                 endgrent();
250         }
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);
255                 }
256         cached_dquots = 0;
257 }
258
259 /* Callback routine called by scan_dquots on each dquot */
260 static int output(struct dquot *dquot, char *name)
261 {
262         if (flags & FL_NONAME) {        /* We should translate names? */
263                 char namebuf[MAXNAMELEN];
264
265                 sprintf(namebuf, "#%u", dquot->dq_id);
266                 print(dquot, namebuf);
267         }
268         else if (name || flags & FL_NOCACHE) {  /* We shouldn't do batched id->name translations? */
269                 char namebuf[MAXNAMELEN];
270
271                 if (!name) {
272                         id2name(dquot->dq_id, dquot->dq_h->qh_type, namebuf);
273                         name = namebuf;
274                 }
275                 print(dquot, name);
276         }
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);
281         }
282         return 0;
283 }
284
285 /* Dump information stored in one quota file */
286 static void report_it(struct quota_handle *h, int type)
287 {
288         char bgbuf[MAXTIMELEN], igbuf[MAXTIMELEN];
289         char *spacehdr;
290
291         if (flags & FL_SHORTNUMS)
292                 spacehdr = _("Space");
293         else
294                 spacehdr = _("Block");
295
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");
303
304         if (h->qh_ops->scan_dquots(h, output) < 0)
305                 return;
306         dump_cached_dquots(type);
307         if (h->qh_ops->report) {
308                 putchar('\n');
309                 h->qh_ops->report(h, flags & FL_VERBOSE);
310                 putchar('\n');
311         }
312 }
313
314 static void report(int type)
315 {
316         struct quota_handle **handles;
317         int i;
318
319         if (flags & FL_ALL)
320                 handles = create_handle_list(0, NULL, type, fmt, IOI_READONLY | IOI_OPENFILE, MS_LOCALONLY | (flags & FL_NOAUTOFS ? MS_NO_AUTOFS : 0));
321         else
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);
326 }
327
328 int main(int argc, char **argv)
329 {
330         gettexton();
331         progname = basename(argv[0]);
332
333         parse_options(argc, argv);
334         init_kernel_interface();
335
336         if (flags & FL_USER)
337                 report(USRQUOTA);
338
339         if (flags & FL_GROUP)
340                 report(GRPQUOTA);
341
342         return 0;
343 }