1 /*
2 * (c) Copyright 1990, Kim Fabricius Storm. All rights reserved.
3 * Copyright (c) 1996-2005 Michael T Pins. All rights reserved.
4 *
5 * Accounting for news reading.
6 *
7 * The nnacct program is called by nn in three cases:
8 *
9 * - on startup (-q) to check for permission to run nn (at this time)
10 * - when the :cost command is executed (-cUSAGE -r) to
11 * produce a "cost sofar" report, and
12 * - at exit (-uUSAGE -r) to add the USAGE to the user's account
13 * and print a "cost" report.
14 *
15 * It can also be invoked by nnusage to print a usage and cost
16 * report for the current user (default), or by the super user
17 * to produce a usage and cost report for all users.
18 */
19
20 #include <unistd.h>
21 #include <stdarg.h>
22 #include <errno.h>
23 #include "config.h"
24 #include "global.h"
25 #include "account.h"
26 #include "execute.h"
27 #include "options.h"
28 #include "proto.h"
29
30 extern char *lib_directory;
31
32 struct account {
33 off_t ac_offset; /* offset in acct file */
34 int ac_found; /* present in acct file */
35
36 char ac_user[24];/* user name */
37
38 long ac_total; /* total usage */
39 time_t ac_last; /* last active */
40 int ac_policy; /* assigned policy */
41 int ac_quota; /* time quota */
42 };
43
44 /* account.c */
45
46 static void put_entry(FILE * acctf, struct account * ac);
47 static int get_entry(FILE * acctf, char *user, struct account * ac);
48 static void do_cost(struct account * ac, int ses);
49 static void do_report(struct account * ac, int hdr);
50 static void do_report_all(FILE * acctf);
51 static void do_zero(void);
52 static int news_admin(char *caller);
53
54 /*
55 * local authorization policy checking
56 *
57 * return/exit values of policy_check (nnacct -P0) are:
58 *
59 * 0: access granted
60 * 1: access granted, but cannot post
61 * 2: access denied (not authorized)
62 * 3: access denied (not allowed at this time of day)
63 * 4: access denied (quota exceeded)
64 */
65
66 #ifdef AUTHORIZE
67 #include <time.h>
68
69 static int
holiday(struct tm * tm)70 holiday(struct tm * tm)
71 {
72 /* Kim's birthday - 23 April */
73 if (tm->tm_mon == 3 && tm->tm_mday == 23)
74 return 1;
75 /* another birthday - 25 December :-) */
76 if (tm->tm_mon == 11 && tm->tm_mday == 25)
77 return 1;
78 /* ... */
79 return 0;
80 }
81
82 static int
policy_check(int policy)83 policy_check(int policy)
84 {
85 struct tm *tm, *localtime();
86 time_t t;
87 int no_post = 0;
88
89 if (policy >= NO_POST) {
90 policy -= NO_POST;
91 no_post = 1;
92 }
93 switch (policy % 10) {
94 case DENY_ACCESS:
95 return 2;
96
97 case ALL_HOURS:
98 case FREE_ACCOUNT:
99 break;
100
101 case OFF_HOURS: /* adapt this to your local requirements */
102 time(&t);
103 tm = localtime(&t);
104 if (tm->tm_wday == 0)
105 break; /* Sunday */
106 if (tm->tm_wday == 6)
107 break; /* Saturday */
108 if (tm->tm_hour < 9)
109 break; /* morning */
110 if (tm->tm_hour > 16)
111 break; /* evening */
112 if (holiday(tm))
113 break; /* holidays */
114 /* etc. */
115 return 3;
116
117 default:
118 return 2;
119 }
120
121 return no_post;
122 }
123
124 #endif
125
126 static int add_usage = -1;
127 static int show_cost = -1;
128 static int report = 0;
129 static int report_all = 0;
130 static int quiet = 0;
131 static char *acct_file = NULL;
132 static int ck_policy = -1;
133 static int new_policy = -1;
134 static int new_quota = -1;
135 static int who_am_caller = I_AM_ACCT;
136 static char *zero_accounts = NULL;
137
Option_Description(acct_options)138 Option_Description(acct_options)
139 {
140 'C', Int_Option(show_cost),
141 'U', Int_Option(add_usage),
142 'W', Int_Option(who_am_caller),
143 'P', Int_Option(ck_policy),
144 'a', Bool_Option(report_all),
145 'f', String_Option(acct_file),
146 'p', Int_Option(new_policy),
147 'q', Int_Option(new_quota),
148 'r', Bool_Option(report),
149 'Q', Bool_Option(quiet),
150 'Z', String_Option(zero_accounts),
151 '\0',
152 };
153
154 /*
155 * Accounting information:
156 *
157 * xxxxxxx 00000000 00000000 00 00000\n
158 *
159 * login time used last pol quota
160 * name (minutes) active icy (hours)
161 *
162 * See the printf/scanf formats later on.
163 */
164
165
166 #define INPUT_FMT "%s %ld %ld %d %d\n"
167 #define OUTPUT_FMT "%s %08ld %08lx %02d %05d\n"
168
169 static int
get_entry(FILE * acctf,char * user,struct account * ac)170 get_entry(FILE * acctf, char *user, struct account * ac)
171 {
172 char line[100];
173
174 if (acctf != NULL && user != NULL)
175 rewind(acctf);
176
177 ac->ac_found = 0;
178
179 for (;;) {
180 ac->ac_policy = DEFAULT_POLICY;
181 ac->ac_last = 0;
182 ac->ac_total = 0;
183 ac->ac_quota = DEFAULT_QUOTA;
184 ac->ac_user[0] = NUL;
185
186 if (acctf == NULL)
187 break;
188
189 ac->ac_offset = ftell(acctf);
190
191 if (fgets(line, 100, acctf) == NULL)
192 break;
193
194 sscanf(line, INPUT_FMT,
195 ac->ac_user, &ac->ac_total, &ac->ac_last,
196 &ac->ac_policy, &ac->ac_quota);
197
198 if (user == NULL)
199 return 1;
200
201 if (strcmp(user, ac->ac_user) == 0) {
202 ac->ac_found = 1;
203 return 1;
204 }
205 }
206
207 if (user != NULL)
208 strcpy(ac->ac_user, user);
209 return 0;
210 }
211
212 static void
put_entry(FILE * acctf,struct account * ac)213 put_entry(FILE * acctf, struct account * ac)
214 {
215 if (ac->ac_found)
216 fseek(acctf, ac->ac_offset, 0);
217 else
218 fseek(acctf, (off_t) 0, 2);
219
220 fprintf(acctf, OUTPUT_FMT,
221 ac->ac_user, ac->ac_total, ac->ac_last,
222 ac->ac_policy, ac->ac_quota);
223 }
224
225 static void
do_cost(struct account * ac,int ses)226 do_cost(struct account * ac, int ses)
227 {
228 long r;
229
230 #ifdef COST_PER_MINUTE
231
232 #ifndef COST_UNIT
233 #define COST_UNIT ""
234 #endif
235
236 if (ses >= 0)
237 printf("Cost this session: %ld %s Period total:",
238 ((long) ses * COST_PER_MINUTE) / 100, COST_UNIT);
239 printf("%6ld %s",
240 (ac->ac_total * COST_PER_MINUTE) / 100, COST_UNIT);
241 #else
242 printf("Time used this session: %d:%02d Period total: %ld:%02ld",
243 ses / 60, ses % 60, ac->ac_total / 60, ac->ac_total % 60);
244 #endif
245
246 if (ses >= 0 && ac->ac_quota > 0) {
247 r = ac->ac_quota - ac->ac_total / 60;
248 printf(" Quota: %ld hour%s", r, plural(r));
249 }
250 fl;
251 }
252
253 static void
do_report(struct account * ac,int hdr)254 do_report(struct account * ac, int hdr)
255 {
256 if (hdr) {
257 printf("USER USAGE QUOTA LAST_ACTIVE");
258
259 #ifdef COST_PER_MINUTE
260 printf(" COST/PERIOD");
261 #endif
262
263 #ifdef AUTHORIZE
264 printf(" POLICY");
265 #endif
266
267 putchar(NL);
268 }
269 printf("%-8.8s %4ld.%02ld %5d %-12.12s ",
270 ac->ac_user,
271 ac->ac_total / 60, ac->ac_total % 60,
272 ac->ac_quota,
273 ac->ac_last ? date_time(ac->ac_last) : "");
274
275 #ifdef COST_PER_MINUTE
276 do_cost(ac, -1);
277 #endif
278
279 #ifdef AUTHORIZE
280 printf(" %2d ", ac->ac_policy);
281 #endif
282
283 printf("\n");
284 }
285
286 static void
do_report_all(FILE * acctf)287 do_report_all(FILE * acctf)
288 {
289 struct account ac;
290 int first = 1;
291
292 while (get_entry(acctf, (char *) NULL, &ac)) {
293 do_report(&ac, first);
294 first = 0;
295 }
296 }
297
298 static char *ZERO_STAMP = "(Zeroed)";
299
300 static void
do_zero(void)301 do_zero(void)
302 {
303 FILE *old, *new;
304 char *acct, bak[FILENAME];
305 struct account ac;
306
307 acct = relative(lib_directory, "acct");
308 old = open_file(acct, OPEN_READ);
309 if (old == NULL)
310 goto err;
311
312 sprintf(bak, "%s.old", acct);
313 if (unlink(bak) < 0 && errno != ENOENT)
314 goto err;
315 if (link(acct, bak) < 0)
316 goto err;
317 if (unlink(acct) < 0) {
318 unlink(bak);
319 goto err;
320 }
321 umask(0177);
322 new = open_file(acct, OPEN_CREATE);
323 if (new == NULL)
324 goto err2;
325 ac.ac_found = 0;
326 strcpy(ac.ac_user, ZERO_STAMP);
327 ac.ac_total = ac.ac_policy = ac.ac_quota = 0;
328 time(&(ac.ac_last));
329 put_entry(new, &ac);
330
331 while (get_entry(old, (char *) NULL, &ac)) {
332 if (strcmp(ac.ac_user, ZERO_STAMP) == 0)
333 continue;
334 ac.ac_total = 0;
335 ac.ac_found = 0;
336 put_entry(new, &ac);
337 }
338 fclose(old);
339 if (fclose(new) == EOF)
340 goto err2;
341 return;
342
343 err2:
344 unlink(acct);
345 if (link(bak, acct) == 0)
346 unlink(bak);
347 err:
348 fprintf(stderr, "ZERO of accounts failed -- check permissions etc.\n");
349 }
350
351 static
352 int
news_admin(char * caller)353 news_admin(char *caller)
354 {
355 FILE *adm;
356 char line[80];
357 int len, ok;
358
359 adm = open_file(relative(lib_directory, "admins"), OPEN_READ);
360 if (adm == NULL)
361 return 2;
362 len = strlen(caller);
363 ok = 0;
364
365 while (fgets(line, 80, adm)) {
366 if (line[len] != NL)
367 continue;
368 line[len] = NUL;
369 if (strcmp(caller, line) == 0) {
370 ok = 1;
371 break;
372 }
373 }
374 fclose(adm);
375 return ok;
376 }
377
378 int
main(int argc,char * argv[])379 main(int argc, char *argv[])
380 {
381 char *caller;
382 FILE *acctf;
383 char *fname = NULL;
384 int users, i;
385 struct account ac, *actab = NULL;
386
387 who_am_i = I_AM_ACCT;
388
389 init_global();
390
391 users = parse_options(argc, argv, (char *) NULL, acct_options, "");
392
393 if (zero_accounts && strcmp(zero_accounts, "ERO")) {
394 fprintf(stderr, "Must specify -ZERO to clear accounts\n");
395 exit(1);
396 }
397 if (user_id != 0) {
398 caller = user_name();
399 if (news_admin(caller) == 1)
400 goto caller_ok;
401
402 if (report_all) {
403 fprintf(stderr, "Only root can request complete reports\n");
404 exit(9);
405 }
406 if (new_policy >= 0) {
407 fprintf(stderr, "Only root can change user authorization\n");
408 exit(9);
409 }
410 if (new_quota >= 0) {
411 fprintf(stderr, "Only root can change user quotas\n");
412 exit(9);
413 }
414 if (zero_accounts) {
415 fprintf(stderr, "Only root can zero user accounts\n");
416 exit(9);
417 }
418 if (users > 0) {
419 fprintf(stderr, "Only root can request reports for other users\n");
420 exit(9);
421 }
422 } else
423 caller = "root";
424
425 caller_ok:
426 if ((new_policy >= 0 || new_quota >= 0) && users == 0) {
427 fprintf(stderr, "usage: %s -pPOLICY -qQUOTA user...\n", argv[0]);
428 exit(1);
429 }
430 if (add_usage == 0 && report) {
431 show_cost = 0;
432 add_usage = -1;
433 }
434 if (zero_accounts || add_usage > 0 || new_policy >= 0 || new_quota >= 0) {
435 if (acct_file) {
436 fprintf(stderr, "Can only update current acct file %s\n", acct_file);
437 exit(2);
438 }
439 proto_lock(I_AM_ACCT, PL_SET_QUICK);
440 }
441 if (zero_accounts) {
442 do_zero();
443 proto_lock(I_AM_ACCT, PL_CLEAR);
444 exit(0);
445 }
446 if (acct_file) {
447 if ((acctf = open_file(acct_file, OPEN_READ)) == NULL)
448 acctf = open_file(relative(lib_directory, acct_file), OPEN_READ);
449 if (acctf == NULL) {
450 fprintf(stderr, "Accounting file %s not found\n", acct_file);
451 if (add_usage > 0 || new_policy >= 0 || new_quota >= 0)
452 proto_lock(I_AM_ACCT, PL_CLEAR);
453 exit(1);
454 }
455 } else {
456 fname = relative(lib_directory, "acct");
457 acctf = open_file(fname, OPEN_READ);
458 }
459
460 if (report_all) {
461 do_report_all(acctf);
462 fclose(acctf);
463 exit(0);
464 }
465 if (ck_policy >= 0) {
466
467 #ifdef AUTHORIZE
468 get_entry(acctf, caller, &ac);
469
470 #ifdef ACCOUNTING
471 if (ac.ac_quota > 0 && ac.ac_quota < ac.ac_total / 60)
472 exit(4);
473 #endif
474
475 exit(policy_check(ac.ac_policy));
476 #else
477 exit(0);
478 #endif
479 }
480 if (show_cost >= 0) {
481 get_entry(acctf, caller, &ac);
482 if (ac.ac_policy == FREE_ACCOUNT)
483 exit(0);
484 ac.ac_total += show_cost;
485 do_cost(&ac, show_cost);
486 exit(0);
487 }
488 if (add_usage > 0) {
489 get_entry(acctf, caller, &ac);
490 if (ac.ac_policy == FREE_ACCOUNT)
491 goto unlock;
492 ac.ac_total += add_usage;
493 time(&ac.ac_last);
494 } else if (users > 0) {
495 actab = newobj(struct account, users + 1);
496 for (i = 1; i <= users; i++) {
497 get_entry(acctf, argv[i], &actab[i]);
498 if (new_policy >= 0 || new_quota >= 0) {
499 if (new_policy >= 0)
500 actab[i].ac_policy = new_policy;
501 if (new_quota >= 0)
502 actab[i].ac_quota = new_quota;
503 } else
504 do_report(&actab[i], i == 1);
505 }
506 } else if (report) {
507 if (get_entry(acctf, caller, &ac))
508 do_report(&ac, 1);
509 exit(0);
510 }
511 if (acctf)
512 fclose(acctf);
513
514 if (add_usage <= 0 && new_policy < 0 && new_quota < 0)
515 exit(0);
516
517 umask(0177);
518 acctf = open_file(fname, OPEN_UPDATE | MUST_EXIST);
519
520 if (new_policy >= 0 || new_quota >= 0) {
521 for (i = 1; i <= users; i++)
522 put_entry(acctf, &actab[i]);
523 fclose(acctf);
524 goto unlock;
525 }
526 if (add_usage > 0) {
527 put_entry(acctf, &ac);
528 if (report) {
529 do_cost(&ac, add_usage);
530 }
531 fclose(acctf);
532
533 #ifdef ACCTLOG
534 fname = relative(lib_directory, "acctlog");
535 acctf = open_file(fname, OPEN_APPEND | MUST_EXIST);
536 fprintf(acctf, "%s\t%s\t%ld\n",
537 caller, date_time(ac.ac_last), (long) add_usage);
538 fclose(acctf);
539 #endif
540
541 goto unlock;
542 }
543 unlock:
544 proto_lock(I_AM_ACCT, PL_CLEAR);
545 exit(0);
546 /* NOTREACHED */
547 }
548
549 void
nn_exit(int n)550 nn_exit(int n)
551 {
552 exit(n);
553 }
554
555 void
nn_exitmsg(int n,char * fmt,...)556 nn_exitmsg(int n, char *fmt,...)
557 {
558 va_list ap;
559
560 va_start(ap, fmt);
561 vprintf(fmt, ap);
562 putchar(NL);
563 va_end(ap);
564
565 nn_exit(n);
566 /* NOTREACHED */
567 }
568
569 /*
570 * dummy routines - should never be called by nnacct
571 */
572
573 int no_update = 0;
574
575 int
set_variable(char * variable,int on,char * val_string)576 set_variable(char *variable, int on, char *val_string)
577 {
578 return 0;
579 }
580
581 void
msg(char * fmt,...)582 msg(char *fmt,...)
583 {
584 }
585
586
587 #ifdef HAVE_JOBCONTROL
588 int
suspend_nn(void)589 suspend_nn(void)
590 {
591 return 0;
592 }
593
594 #endif
595