]>
Commit | Line | Data |
---|---|---|
5aead01d | 1 | /* |
da23017d NS |
2 | * Copyright (c) 2005 Silicon Graphics, Inc. |
3 | * All Rights Reserved. | |
5aead01d | 4 | * |
da23017d NS |
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 | |
5aead01d NS |
7 | * published by the Free Software Foundation. |
8 | * | |
da23017d NS |
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. | |
5aead01d | 13 | * |
da23017d NS |
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 | |
5aead01d NS |
17 | */ |
18 | ||
cfe6e3f0 | 19 | #include <sys/types.h> |
29647c8d | 20 | #include <stdbool.h> |
5aead01d NS |
21 | #include <pwd.h> |
22 | #include <grp.h> | |
23 | #include <utmp.h> | |
2a1888c5 | 24 | #include "init.h" |
5aead01d NS |
25 | #include "quota.h" |
26 | ||
27 | #define SECONDS_IN_A_DAY (24 * 60 * 60) | |
28 | #define SECONDS_IN_A_HOUR (60 * 60) | |
29 | #define SECONDS_IN_A_MINUTE (60) | |
30 | ||
31 | char * | |
32 | time_to_string( | |
44214b5a | 33 | time_t origin, |
5aead01d NS |
34 | uint flags) |
35 | { | |
36 | static char timestamp[32]; | |
37 | time_t now, timer; | |
38 | uint days, hours, minutes, seconds; | |
39 | ||
40 | if (flags & ABSOLUTE_FLAG) { | |
41 | timer = origin; | |
42 | } else { | |
43 | time(&now); | |
44 | timer = MAX(origin - now, 0); | |
45 | } | |
0df88e17 | 46 | |
f61be1b4 ES |
47 | /* |
48 | * If we are in verbose mode, or if less than a day remains, we | |
49 | * will show "X days hh:mm:ss" so the user knows the exact timer status. | |
50 | * | |
51 | * Otherwise, we round down to the nearest day - so we add 30s here | |
52 | * such that setting and reporting a limit in rapid succession will | |
53 | * show the limit which was just set, rather than immediately reporting | |
54 | * one day less. | |
55 | */ | |
56 | if ((timer > SECONDS_IN_A_DAY) && !(flags & VERBOSE_FLAG)) | |
57 | timer += 30; /* seconds */ | |
58 | ||
5aead01d NS |
59 | days = timer / SECONDS_IN_A_DAY; |
60 | if (days) | |
61 | timer %= SECONDS_IN_A_DAY; | |
62 | hours = timer / SECONDS_IN_A_HOUR; | |
63 | if (hours) | |
64 | timer %= SECONDS_IN_A_HOUR; | |
65 | minutes = timer / SECONDS_IN_A_MINUTE; | |
66 | seconds = timer % SECONDS_IN_A_MINUTE; | |
67 | ||
68 | if (flags & LIMIT_FLAG) { | |
69 | snprintf(timestamp, sizeof(timestamp), (flags & HUMAN_FLAG) ? | |
70 | _("[-none-]") : _("[--none--]")); | |
71 | } else if (origin == 0) { | |
72 | snprintf(timestamp, sizeof(timestamp), (flags & HUMAN_FLAG) ? | |
73 | _("[------]") : _("[--------]")); | |
74 | } else if ((hours == 0 && minutes == 0 && seconds == 0) || | |
75 | (!(flags & VERBOSE_FLAG) && days > 0)) { | |
76 | snprintf(timestamp, sizeof(timestamp), "[%u %s]", | |
77 | days, days == 1 ? _("day") : _("days")); | |
78 | } else if (flags & VERBOSE_FLAG) { | |
79 | snprintf(timestamp, sizeof(timestamp), "[%u %s %02u:%02u:%02u]", | |
80 | days, days == 1 ? _("day") : _("days"), | |
81 | hours, minutes, seconds); | |
82 | } else { /* non-verbose, less than a day remaining */ | |
83 | snprintf(timestamp, sizeof(timestamp), | |
84 | (flags & HUMAN_FLAG) ? | |
85 | "%02u:%02u:%02u" : "[%02u:%02u:%02u]", | |
86 | hours, minutes, seconds); | |
87 | } | |
88 | return timestamp; | |
89 | } | |
90 | ||
91 | static int | |
92 | round_snprintf( | |
93 | char *sp, | |
94 | size_t size, | |
95 | const char *fmt_round, | |
96 | const char *fmt_not_round, | |
97 | __uint64_t value, | |
98 | __uint64_t divisor) | |
99 | { | |
100 | double v = (double)value / divisor; | |
101 | ||
102 | value /= divisor; | |
103 | if (v == (double)value) | |
104 | return snprintf(sp, size, fmt_round, (uint)value); | |
105 | else | |
106 | return snprintf(sp, size, fmt_not_round, v); | |
107 | } | |
108 | ||
109 | /* Basic blocks (512) bytes are returned from quotactl */ | |
110 | #define BBS_TO_EXABYTES(bbs) ((__uint64_t)(bbs)>>51) | |
111 | #define BBS_TO_PETABYTES(bbs) ((__uint64_t)(bbs)>>41) | |
112 | #define BBS_TO_TERABYTES(bbs) ((__uint64_t)(bbs)>>31) | |
113 | #define BBS_TO_GIGABYTES(bbs) ((__uint64_t)(bbs)>>21) | |
114 | #define BBS_TO_MEGABYTES(bbs) ((__uint64_t)(bbs)>>11) | |
115 | #define BBS_TO_KILOBYTES(bbs) ((__uint64_t)(bbs)>>1) | |
116 | ||
117 | #define BBEXABYTE ((__uint64_t)1<<51) | |
118 | #define BBPETABYTE ((__uint64_t)1<<41) | |
119 | #define BBTERABYTE ((__uint64_t)1<<31) | |
120 | #define BBGIGABYTE ((__uint64_t)1<<21) | |
121 | #define BBMEGABYTE ((__uint64_t)1<<11) | |
122 | #define BBKILOBYTE ((__uint64_t)1<< 1) | |
123 | ||
124 | char * | |
125 | bbs_to_string( | |
126 | __uint64_t v, | |
127 | char *sp, | |
128 | uint size) | |
129 | { | |
130 | if (v == 0) | |
131 | snprintf(sp, size, "%4u", (uint)v); | |
132 | else if (BBS_TO_EXABYTES(v)) | |
133 | round_snprintf(sp, size, "%3uE", "%3.1fE", v, BBEXABYTE); | |
134 | else if (BBS_TO_PETABYTES(v)) | |
135 | round_snprintf(sp, size, "%3uP", "%3.1fP", v, BBPETABYTE); | |
136 | else if (BBS_TO_TERABYTES(v)) | |
137 | round_snprintf(sp, size, "%3uT", "%3.1fT", v, BBTERABYTE); | |
138 | else if (BBS_TO_GIGABYTES(v)) | |
139 | round_snprintf(sp, size, "%3uG", "%3.1fG", v, BBGIGABYTE); | |
140 | else if (BBS_TO_MEGABYTES(v)) | |
141 | round_snprintf(sp, size, "%3uM", "%3.1fM", v, BBMEGABYTE); | |
142 | else if (BBS_TO_KILOBYTES(v)) | |
143 | round_snprintf(sp, size, "%3uK", "%3.1fK", v, BBKILOBYTE); | |
144 | else | |
145 | snprintf(sp, size, "%4u", (uint)v << BBSHIFT); /* bytes */ | |
146 | return sp; | |
147 | } | |
148 | ||
149 | #define THOUSAND ((__uint64_t)1000) | |
150 | #define MILLION ((__uint64_t)1000*1000) | |
151 | #define BILLION ((__uint64_t)1000*1000*1000) | |
152 | #define TRILLION ((__uint64_t)1000*1000*1000*1000) | |
153 | #define GAZILLION ((__uint64_t)1000*1000*1000*1000*1000) | |
154 | #define RIDICULOUS ((__uint64_t)1000*1000*1000*1000*1000*1000) | |
155 | #define STOPALREADY ((__uint64_t)1000*1000*1000*1000*1000*1000*1000) | |
156 | ||
157 | char * | |
158 | num_to_string( | |
159 | __uint64_t v, | |
160 | char *sp, | |
161 | uint size) | |
162 | { | |
163 | if (v == 0) | |
164 | snprintf(sp, size, "%4u", (uint)v); | |
165 | else if (v > STOPALREADY) | |
166 | round_snprintf(sp, size, "%3us", "%3.1fs", v, STOPALREADY); | |
167 | else if (v > RIDICULOUS) | |
168 | round_snprintf(sp, size, "%3ur", "%3.1fr", v, RIDICULOUS); | |
169 | else if (v > GAZILLION) | |
170 | round_snprintf(sp, size, "%3ug", "%3.1fg", v, GAZILLION); | |
171 | else if (v > TRILLION) | |
172 | round_snprintf(sp, size, "%3ut", "%3.1ft", v, TRILLION); | |
173 | else if (v > BILLION) | |
174 | round_snprintf(sp, size, "%3ub", "%3.1fb", v, BILLION); | |
175 | else if (v > MILLION) | |
176 | round_snprintf(sp, size, "%3um", "%3.1fm", v, MILLION); | |
177 | else if (v > THOUSAND) | |
178 | round_snprintf(sp, size, "%3uk", "%3.1fk", v, THOUSAND); | |
179 | else | |
180 | snprintf(sp, size, "%4u", (uint)v); | |
181 | return sp; | |
182 | } | |
183 | ||
184 | char * | |
185 | pct_to_string( | |
481d1f83 AE |
186 | __uint64_t portion, |
187 | __uint64_t whole, | |
188 | char *buf, | |
5aead01d NS |
189 | uint size) |
190 | { | |
481d1f83 AE |
191 | uint percent; |
192 | ||
193 | percent = whole ? (uint) (100.0 * portion / whole + 0.5) : 0; | |
194 | if (snprintf(buf, size, "%3u", percent) < 0) | |
195 | return "???"; | |
196 | ||
197 | return buf; | |
5aead01d NS |
198 | } |
199 | ||
200 | char * | |
201 | form_to_string( | |
202 | uint form) | |
203 | { | |
8b3de41d | 204 | char *forms[] = { |
5aead01d NS |
205 | _("Blocks"), _("Inodes"), _("Realtime Blocks") }; |
206 | ||
207 | if (form & XFS_BLOCK_QUOTA) | |
208 | return forms[0]; | |
209 | if (form & XFS_INODE_QUOTA) | |
210 | return forms[1]; | |
211 | if (form & XFS_RTBLOCK_QUOTA) | |
212 | return forms[2]; | |
213 | return NULL; | |
214 | } | |
215 | ||
216 | char * | |
217 | type_to_string( | |
218 | uint type) | |
219 | { | |
8b3de41d | 220 | char *types[] = { _("User"), _("Group"), _("Project") }; |
5aead01d NS |
221 | |
222 | if (type & XFS_USER_QUOTA) | |
223 | return types[0]; | |
224 | if (type & XFS_GROUP_QUOTA) | |
225 | return types[1]; | |
226 | if (type & XFS_PROJ_QUOTA) | |
227 | return types[2]; | |
228 | return NULL; | |
229 | } | |
230 | ||
231 | ||
232 | /* | |
233 | * Identifier caches - user/group/project names/IDs | |
234 | */ | |
235 | ||
236 | #define NID 4096 | |
237 | #define IDMASK (NID-1) | |
238 | ||
239 | typedef struct { | |
240 | __uint32_t id; | |
0717a7db | 241 | char name[NMAX+1]; |
5aead01d NS |
242 | } idcache_t; |
243 | ||
244 | static idcache_t uidnc[NID]; | |
245 | static idcache_t gidnc[NID]; | |
246 | static idcache_t pidnc[NID]; | |
247 | static int uentriesleft = NID; | |
248 | static int gentriesleft = NID; | |
249 | static int pentriesleft = NID; | |
250 | ||
251 | static idcache_t * | |
252 | getnextpwent( | |
253 | __uint32_t id, | |
254 | int byid) | |
255 | { | |
256 | struct passwd *pw; | |
257 | static idcache_t idc; | |
258 | ||
259 | /* /etc/passwd */ | |
260 | if ((pw = byid? getpwuid(id) : getpwent()) == NULL) | |
261 | return NULL; | |
262 | idc.id = pw->pw_uid; | |
0717a7db | 263 | strncpy(idc.name, pw->pw_name, NMAX); |
5aead01d NS |
264 | return &idc; |
265 | } | |
266 | ||
267 | static idcache_t * | |
268 | getnextgrent( | |
269 | __uint32_t id, | |
270 | int byid) | |
271 | { | |
272 | struct group *gr; | |
273 | static idcache_t idc; | |
274 | ||
275 | if ((gr = byid? getgrgid(id) : getgrent()) == NULL) | |
276 | return NULL; | |
277 | idc.id = gr->gr_gid; | |
0717a7db | 278 | strncpy(idc.name, gr->gr_name, NMAX); |
5aead01d NS |
279 | return &idc; |
280 | } | |
281 | ||
282 | static idcache_t * | |
283 | getnextprent( | |
284 | __uint32_t id, | |
285 | int byid) | |
286 | { | |
287 | fs_project_t *pr; | |
288 | static idcache_t idc; | |
289 | ||
290 | if ((pr = byid? getprprid(id) : getprent()) == NULL) | |
291 | return NULL; | |
292 | idc.id = pr->pr_prid; | |
0717a7db | 293 | strncpy(idc.name, pr->pr_name, NMAX); |
5aead01d NS |
294 | return &idc; |
295 | } | |
296 | ||
297 | char * | |
298 | uid_to_name( | |
299 | __uint32_t id) | |
300 | { | |
301 | idcache_t *ncp, *idp; | |
302 | ||
303 | /* Check cache for name first */ | |
304 | ncp = &uidnc[id & IDMASK]; | |
305 | if (ncp->id == id && ncp->name[0]) | |
306 | return ncp->name; | |
307 | if (uentriesleft) { | |
308 | /* | |
309 | * Fill this cache while seaching for a name. | |
310 | * This lets us run through the file serially. | |
311 | */ | |
312 | if (uentriesleft == NID) | |
313 | setpwent(); | |
314 | while (((idp = getnextpwent(id, 0)) != NULL) && uentriesleft) { | |
315 | uentriesleft--; | |
316 | ncp = &uidnc[idp->id & IDMASK]; | |
317 | if (ncp->name[0] == '\0' || idp->id == id) | |
318 | memcpy(ncp, idp, sizeof(idcache_t)); | |
319 | if (idp->id == id) | |
320 | return ncp->name; | |
321 | } | |
322 | endpwent(); | |
323 | uentriesleft = 0; | |
324 | ncp = &uidnc[id & IDMASK]; | |
325 | } | |
326 | ||
327 | /* Not cached - do it the slow way & insert into cache */ | |
328 | if ((idp = getnextpwent(id, 1)) == NULL) | |
329 | return NULL; | |
330 | memcpy(ncp, idp, sizeof(idcache_t)); | |
331 | return ncp->name; | |
332 | } | |
333 | ||
334 | char * | |
335 | gid_to_name( | |
336 | __uint32_t id) | |
337 | { | |
338 | idcache_t *ncp, *idp; | |
339 | ||
340 | /* Check cache for name first */ | |
341 | ncp = &gidnc[id & IDMASK]; | |
342 | if (ncp->id == id && ncp->name[0]) | |
343 | return ncp->name; | |
344 | if (gentriesleft) { | |
345 | /* | |
346 | * Fill this cache while seaching for a name. | |
347 | * This lets us run through the file serially. | |
348 | */ | |
349 | if (gentriesleft == NID) | |
350 | setgrent(); | |
351 | while (((idp = getnextgrent(id, 0)) != NULL) && gentriesleft) { | |
352 | gentriesleft--; | |
353 | ncp = &gidnc[idp->id & IDMASK]; | |
354 | if (ncp->name[0] == '\0' || idp->id == id) | |
355 | memcpy(ncp, idp, sizeof(idcache_t)); | |
356 | if (idp->id == id) | |
357 | return ncp->name; | |
358 | } | |
359 | endgrent(); | |
360 | gentriesleft = 0; | |
361 | ncp = &gidnc[id & IDMASK]; | |
362 | } | |
363 | ||
364 | /* Not cached - do it the slow way & insert into cache */ | |
365 | if ((idp = getnextgrent(id, 1)) == NULL) | |
366 | return NULL; | |
367 | memcpy(ncp, idp, sizeof(idcache_t)); | |
368 | return ncp->name; | |
369 | } | |
370 | ||
371 | char * | |
372 | prid_to_name( | |
373 | __uint32_t id) | |
374 | { | |
375 | idcache_t *ncp, *idp; | |
376 | ||
377 | /* Check cache for name first */ | |
378 | ncp = &pidnc[id & IDMASK]; | |
379 | if (ncp->id == id && ncp->name[0]) | |
380 | return ncp->name; | |
381 | if (pentriesleft) { | |
382 | /* | |
383 | * Fill this cache while seaching for a name. | |
384 | * This lets us run through the file serially. | |
385 | */ | |
386 | if (pentriesleft == NID) | |
387 | setprent(); | |
388 | while (((idp = getnextprent(id, 0)) != NULL) && pentriesleft) { | |
389 | pentriesleft--; | |
390 | ncp = &pidnc[idp->id & IDMASK]; | |
391 | if (ncp->name[0] == '\0' || idp->id == id) | |
392 | memcpy(ncp, idp, sizeof(idcache_t)); | |
393 | if (idp->id == id) | |
394 | return ncp->name; | |
395 | } | |
396 | endprent(); | |
397 | pentriesleft = 0; | |
398 | ncp = &pidnc[id & IDMASK]; | |
399 | } | |
400 | ||
401 | /* Not cached - do it the slow way & insert into cache */ | |
402 | if ((idp = getnextprent(id, 1)) == NULL) | |
403 | return NULL; | |
404 | memcpy(ncp, idp, sizeof(idcache_t)); | |
405 | return ncp->name; | |
406 | } | |
407 | ||
408 | ||
409 | /* | |
410 | * Utility routine for opening an output file so that it can | |
411 | * be "securely" written to (i.e. without vulnerability to a | |
412 | * symlink attack). | |
413 | * | |
414 | * Returns NULL on failure, stdout on NULL input. | |
415 | */ | |
416 | FILE * | |
417 | fopen_write_secure( | |
418 | char *fname) | |
419 | { | |
420 | FILE *fp; | |
421 | int fd; | |
422 | ||
423 | if (!fname) | |
424 | return stdout; | |
425 | ||
426 | if ((fd = open(fname, O_CREAT|O_WRONLY|O_EXCL, 0600)) < 0) { | |
e3210fd8 | 427 | exitcode = 1; |
5aead01d NS |
428 | fprintf(stderr, _("%s: open on %s failed: %s\n"), |
429 | progname, fname, strerror(errno)); | |
430 | return NULL; | |
431 | } | |
432 | if ((fp = fdopen(fd, "w")) == NULL) { | |
e3210fd8 | 433 | exitcode = 1; |
5aead01d NS |
434 | fprintf(stderr, _("%s: fdopen on %s failed: %s\n"), |
435 | progname, fname, strerror(errno)); | |
436 | close(fd); | |
437 | return NULL; | |
438 | } | |
439 | return fp; | |
440 | } |