This is account.c in view mode; [Download] [Up]
/* * (c) Copyright 1990, Kim Fabricius Storm. All rights reserved. * * Accounting for news reading. * * The nnacct program is called by nn it three cases: * * - on startup (-q) to check for permission to run nn (at this time) * - when the :cost command is executed (-cUSAGE -r) to * produce a "cost sofar" report, and * - at exit (-uUSAGE -r) to add the USAGE to the user's account * and print a "cost" report. * * It can also be invoked by nnusage to print a usage and cost * report for the current user (default), or by the super user * to produce a usage and cost report for all users. * * Accumulated accounting information is saved in the file $DB/acct. * * If ACCTLOG is defined, a sequential log is maintained in $DB/acctlog. * * You can define a COST_PER_MINUTE and a COST_UNIT to make * the user get a cost calculation (maybe just for the fun of it - * you can't imagine how expensive it is to read news here :-). * * The COST_PER_MINUTE should be the price per minute multiplied * by 100 (to allow prices like $0.03/minute). * The definitions below corresponds to 1 Kroner per minute. * * If COST_PER_MINUTE is not defined (you can #undef it in config.h), * a "Time used" rather than a "Cost" report is produced. */ #define ACCTLOG /* */ #define COST_PER_MINUTE 100 /* price(in UNITs)/min * 100 */ #define COST_UNIT "Kr." /* Currency */ #include "config.h" #include "options.h" #include "proto.h" #include <errno.h> import char *db_directory; /* * local authorization policy checking * * return/exit values of policy_check (nnacct -P0) are: * * 0: access granted * 1: access granted, but cannot post * 2: access denied (not authorized) * 3: access denied (not allowed at this time of day) * 4: access denied (quota exceeded) */ #define DENY_ACCESS 0 #define FREE_ACCOUNT 1 #define ALL_HOURS 2 #define OFF_HOURS 3 #define NO_POST 40 /* add if cannot post */ /* * DEFAULT POLICY AND QUOTA FOR NEW USERS * * Notice that QUOTA is measued in hours. * Both ACCOUNTING and AUTHORIZATION must be defined for * the quota mechanism to work. */ #define DEFAULT_POLICY ALL_HOURS /* all time w/accounting */ #define DEFAULT_QUOTA 0 /* unlimited use */ #ifdef AUTHORIZE #include <time.h> static holiday(tm) struct tm *tm; { if (tm->tm_mon == 3 && tm->tm_mday == 23) return 1; /* my birthday */ if (tm->tm_mon == 11 && tm->tm_mday == 25) return 1; /* another birthday */ /* ... */ return 0; } static policy_check(policy) int policy; { struct tm *tm, *localtime(); time_t t; int no_post = 0; if (policy >= NO_POST) { policy -= NO_POST; no_post = 1; } switch (policy % 10) { case DENY_ACCESS: return 2; case ALL_HOURS: case FREE_ACCOUNT: break; case OFF_HOURS: /* adapt this to your local requirements */ time(&t); tm = localtime(&t); if (tm->tm_wday == 0) break; /* Sunday */ if (tm->tm_wday == 6) break; /* Saturday */ if (tm->tm_hour < 9) break; /* morning */ if (tm->tm_hour > 16) break; /* evening */ if (holiday(tm)) break; /* holidays */ /* etc. */ return 3; default: return 2; } return no_post; } #endif static int add_usage = -1; static int show_cost = -1; static int report = 0; static int report_all = 0; static int quiet = 0; static char *acct_file = NULL; static int ck_policy = -1; static int new_policy = -1; static int new_quota = -1; static int who_am_caller = I_AM_ACCT; static char *zero_accounts = NULL; Option_Description(acct_options) { 'C', Int_Option(show_cost), 'U', Int_Option(add_usage), 'W', Int_Option(who_am_caller), 'P', Int_Option(ck_policy), 'a', Bool_Option(report_all), 'f', String_Option(acct_file), 'p', Int_Option(new_policy), 'q', Int_Option(new_quota), 'r', Bool_Option(report), 'Q', Bool_Option(quiet), 'Z', String_Option(zero_accounts), '\0', }; /* * Accounting information: * * xxxxxxx 00000000 00000000 00 00000\n * * login time used last pol quota * name (minutes) active icy (hours) * * See the printf/scanf formats later on. */ #define INPUT_FMT "%s %ld %lx %d %d\n" #define OUTPUT_FMT "%s %08ld %08lx %02d %05d\n" struct account { off_t ac_offset; /* offset in acct file */ int ac_found; /* present in acct file */ char ac_user[24]; /* user name */ long ac_total; /* total usage */ time_t ac_last; /* last active */ int ac_policy; /* assigned policy */ int ac_quota; /* time quota */ }; static get_entry(acctf, user, ac) FILE *acctf; char *user; struct account *ac; { char line[100]; if (acctf != NULL && user != NULL) rewind(acctf); ac->ac_found = 0; for (;;) { ac->ac_policy = DEFAULT_POLICY; ac->ac_last = 0; ac->ac_total = 0; ac->ac_quota = DEFAULT_QUOTA; ac->ac_user[0] = NUL; if (acctf == NULL) break; ac->ac_offset = ftell(acctf); if (fgets(line, 100, acctf) == NULL) break; sscanf(line, INPUT_FMT, ac->ac_user, &ac->ac_total, &ac->ac_last, &ac->ac_policy, &ac->ac_quota); if (user == NULL) return 1; if (strcmp(user, ac->ac_user) == 0) { ac->ac_found = 1; return 1; } } if (user != NULL) strcpy(ac->ac_user, user); return 0; } put_entry(acctf, ac) FILE *acctf; struct account *ac; { if (ac->ac_found) fseek(acctf, ac->ac_offset, 0); else fseek(acctf, (off_t)0, 2); fprintf(acctf, OUTPUT_FMT, ac->ac_user, ac->ac_total, ac->ac_last, ac->ac_policy, ac->ac_quota); } static do_cost(ac, ses) struct account *ac; int ses; { long r; #ifdef COST_PER_MINUTE #ifndef COST_UNIT #define COST_UNIT "" #endif if (ses >= 0) printf("Cost this session: %ld %s Period total:", ((long)ses * COST_PER_MINUTE) / 100, COST_UNIT); printf("%6ld %s", (ac->ac_total * COST_PER_MINUTE) / 100, COST_UNIT); #else printf("Time used this session: %d.%02d Period total: %ld.%02ld", ses/60, ses%60, ac->ac_total/60, ac->ac_total%60); #endif if (ses >= 0 && ac->ac_quota > 0) { r = ac->ac_quota - ac->ac_total/60; printf(" Quota: %ld hour%s", r, plural(r)); } fl; } static do_report(ac, hdr) struct account *ac; int hdr; { if (hdr) { printf("USER USAGE QUOTA LAST_ACTIVE"); #ifdef COST_PER_MINUTE printf(" COST/PERIOD"); #endif #ifdef AUTHORIZE printf(" POLICY"); #endif putchar(NL); } printf("%-8.8s %4ld.%02ld %5d %-12.12s ", ac->ac_user, ac->ac_total/60, ac->ac_total%60, ac->ac_quota, ac->ac_last ? date_time(ac->ac_last) : ""); #ifdef COST_PER_MINUTE do_cost(ac, -1); #endif #ifdef AUTHORIZE printf(" %2d ", ac->ac_policy); #endif printf("\n"); } static do_report_all(acctf) FILE *acctf; { struct account ac; int first = 1; while (get_entry(acctf, (char *)NULL, &ac)) { do_report(&ac, first); first = 0; } } static char *ZERO_STAMP = "(Zeroed)"; static do_zero() { FILE *old, *new; char *acct, bak[FILENAME]; struct account ac; extern int errno; acct = relative(db_directory, "acct"); old = open_file(acct, OPEN_READ); if (old == NULL) goto err; sprintf(bak, "%s.old", acct); if (unlink(bak) < 0 && errno != ENOENT) goto err; if (link(acct, bak) < 0) goto err; if (unlink(acct) < 0) { unlink(bak); goto err; } umask(0177); new = open_file(acct, OPEN_CREATE); if (new == NULL) goto err2; ac.ac_found = 0; strcpy(ac.ac_user, ZERO_STAMP); ac.ac_total = ac.ac_policy = ac.ac_quota = 0; time(&(ac.ac_last)); put_entry(new, &ac); while (get_entry(old, (char *)NULL, &ac)) { if (strcmp(ac.ac_user, ZERO_STAMP) == 0) continue; ac.ac_total = 0; ac.ac_found = 0; put_entry(new, &ac); } fclose(old); if (fclose(new) == EOF) goto err2; return; err2: unlink(acct); if (link(bak, acct) == 0) unlink(bak); err: fprintf(stderr, "ZERO of accounts failed -- check permissions etc.\n"); } static news_admin(caller) char *caller; { FILE *adm; char line[80]; int len, ok; adm = open_file(relative(lib_directory, "admins"), OPEN_READ); if (adm == NULL) return 2; len = strlen(caller); ok = 0; while (fgets(line, 80, adm)) { if (line[len] != NL) continue; line[len] = NUL; if (strcmp(caller, line) == 0) { ok = 1; break; } } fclose(adm); return ok; } main(argc, argv) int argc; char *argv[]; { char *caller, *getlogin(); FILE *acctf; char *fname; int users, i; struct account ac, *actab; who_am_i = I_AM_ACCT; init_global(); users = parse_options(argc, argv, (char *)NULL, acct_options, ""); if (zero_accounts && strcmp(zero_accounts, "ERO")) { fprintf(stderr, "Must specify -ZERO to clear accounts\n"); exit(1); } if (user_id != 0) { caller = user_name(); if (news_admin(caller) == 1) goto caller_ok; if (report_all) { fprintf(stderr, "Only root can request complete reports\n"); exit(9); } if (new_policy >= 0) { fprintf(stderr, "Only root can change user authorization\n"); exit(9); } if (new_quota >= 0) { fprintf(stderr, "Only root can change user quotas\n"); exit(9); } if (zero_accounts) { fprintf(stderr, "Only root can zero user accounts\n"); exit(9); } if (users > 0) { fprintf(stderr, "Only root can request reports for other users\n"); exit(9); } } else caller = "root"; caller_ok: if ((new_policy >= 0 || new_quota >= 0) && users == 0) { fprintf(stderr, "usage: %s -pPOLICY -qQUOTA user...\n", argv[0]); exit(1); } if (add_usage == 0 && report) { show_cost = 0; add_usage = -1; } if (zero_accounts || add_usage > 0 || new_policy >= 0 || new_quota >= 0) { if (acct_file) { fprintf(stderr, "Can only update current acct file\n", acct_file); exit(2); } proto_lock(I_AM_ACCT, PL_SET_QUICK); } if (zero_accounts) { do_zero(); proto_lock(I_AM_ACCT, PL_CLEAR); exit(0); } if (acct_file) { if ((acctf = open_file(acct_file, OPEN_READ)) == NULL) acctf = open_file(relative(db_directory, acct_file), OPEN_READ); if (acctf == NULL) { fprintf(stderr, "Accounting file %s not found\n", acct_file); if (add_usage > 0 || new_policy >= 0 || new_quota >= 0) proto_lock(I_AM_ACCT, PL_CLEAR); exit(1); } } else { fname = relative(db_directory, "acct"); acctf = open_file(fname, OPEN_READ); } if (report_all) { do_report_all(acctf); fclose(acctf); exit(0); } if (ck_policy >= 0) { #ifdef AUTHORIZE get_entry(acctf, caller, &ac); #ifdef ACCOUNTING if (ac.ac_quota > 0 && ac.ac_quota < ac.ac_total/60) exit(4); #endif exit(policy_check(ac.ac_policy)); #else exit(0); #endif } if (show_cost >= 0) { get_entry(acctf, caller, &ac); if (ac.ac_policy == FREE_ACCOUNT) exit(0); ac.ac_total += show_cost; do_cost(&ac, show_cost); exit(0); } if (add_usage > 0) { get_entry(acctf, caller, &ac); if (ac.ac_policy == FREE_ACCOUNT) goto unlock; ac.ac_total += add_usage; time(&ac.ac_last); } else if (users > 0) { actab = newobj(struct account, users + 1); for (i = 1; i <= users; i++) { get_entry(acctf, argv[i], &actab[i]); if (new_policy >= 0 || new_quota >= 0) { if (new_policy >= 0) actab[i].ac_policy = new_policy; if (new_quota >= 0) actab[i].ac_quota = new_quota; } else do_report(&actab[i], i == 1); } } else if (report) { if (get_entry(acctf, caller, &ac)) do_report(&ac, 1); exit(0); } if (acctf) fclose(acctf); if (add_usage <= 0 && new_policy < 0 && new_quota < 0) exit(0); umask(0177); acctf = open_file(fname, OPEN_UPDATE | MUST_EXIST); if (new_policy >= 0 || new_quota >= 0) { for (i = 1; i <= users; i++) put_entry(acctf, &actab[i]); fclose(acctf); goto unlock; } if (add_usage > 0) { put_entry(acctf, &ac); if (report) { do_cost(&ac, add_usage); } fclose(acctf); #ifdef ACCTLOG fname = relative(db_directory, "acctlog"); acctf = open_file(fname, OPEN_APPEND | MUST_EXIST); fprintf(acctf, "%s\t%s\t%ld\n", caller, date_time(ac.ac_last), (long)add_usage); fclose(acctf); #endif goto unlock; } unlock: proto_lock(I_AM_ACCT, PL_CLEAR); exit(0); /*NOTREACHED*/ } nn_exit(n) { exit(n); } user_error() { exit(2); } #ifdef HAVE_JOBCONTROL suspend_nn() {} #endif
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.