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