home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume22 / nn6.4 / part17 / account.c next >
Encoding:
C/C++ Source or Header  |  1990-06-07  |  10.0 KB  |  474 lines

  1. /*
  2.  *    (c) Copyright 1990, Kim Fabricius Storm.  All rights reserved.
  3.  *
  4.  *    Accounting for news reading.
  5.  *
  6.  *    The nnacct program is called by nn it three cases:
  7.  *
  8.  *    - on startup (-q) to check for permission to run nn (at this time)
  9.  *    - when the :cost command is executed (-cUSAGE -r) to
  10.  *      produce a "cost sofar" report, and
  11.  *    - at exit (-uUSAGE -r) to add the USAGE to the user's account
  12.  *      and print a "cost" report.
  13.  *
  14.  *    It can also be invoked by nnusage to print a usage and cost
  15.  *    report for the current user (default), or by the super user
  16.  *    to produce a usage and cost report for all users.
  17.  *
  18.  *    Accumulated accounting information is saved in the file $DB/acct.
  19.  *
  20.  *    If ACCTLOG is defined, a sequential log is maintained in $DB/acctlog.
  21.  *
  22.  *    You can define a COST_PER_MINUTE and a COST_UNIT to make
  23.  *    the user get a cost calculation (maybe just for the fun of it -
  24.  *    you can't imagine how expensive it is to read news here :-).
  25.  *
  26.  *    The COST_PER_MINUTE should be the price per minute multiplied
  27.  *    by 100 (to allow prices like $0.03/minute).
  28.  *    The definitions below corresponds to 1 Kroner per minute.
  29.  */
  30.  
  31. #define ACCTLOG            /* */
  32. #define COST_PER_MINUTE    100    /* price(in UNITs)/min * 100 */
  33. #define COST_UNIT        "Kr."    /* Currency */
  34.  
  35. #include "config.h"
  36. #include "options.h"
  37. #include "proto.h"
  38.  
  39. import char *db_directory;
  40.  
  41.  
  42. /*
  43.  *     local authorization policy checking
  44.  *
  45.  *    return/exit values of policy_check (nnacct -P0) are:
  46.  *
  47.  *    0: access granted
  48.  *    1: access granted, but cannot post
  49.  *    2: access denied (not authorized)
  50.  *    3: access denied (not allowed at this time of day)
  51.  *    4: access denied (quota exceeded)
  52.  */
  53.  
  54. #define DENY_ACCESS    0
  55. #define FREE_ACCOUNT    1
  56. #define ALL_HOURS    2
  57. #define OFF_HOURS    3
  58.  
  59. #define NO_POST        40    /* add if cannot post */
  60.  
  61. /*
  62.  *    DEFAULT POLICY AND QUOTA FOR NEW USERS
  63.  *
  64.  *    Notice that QUOTA is measued in hours.
  65.  *    Both ACCOUNTING and AUTHORIZATION must be defined for
  66.  *    the quota mechanism to work.
  67.  */
  68.  
  69. #define DEFAULT_POLICY     ALL_HOURS    /* all time w/accounting */
  70. #define DEFAULT_QUOTA     0        /* unlimited use */
  71.  
  72. #ifdef AUTHORIZE
  73.  
  74. #include <time.h>
  75.  
  76. static holiday(tm)
  77. struct tm *tm;
  78. {
  79.     if (tm->tm_mon == 3 && tm->tm_mday == 23) return 1;    /* my birthday */
  80.     if (tm->tm_mon == 11 && tm->tm_mday == 25) return 1;    /* another birthday */
  81.     /* ... */
  82.     return 0;
  83. }
  84.  
  85. static policy_check(policy)
  86. int policy;
  87. {
  88.     struct tm *tm, *localtime();
  89.     time_t t;
  90.     int no_post = 0;
  91.  
  92.     if (policy >= NO_POST) {
  93.     policy -= NO_POST;
  94.     no_post = 1;
  95.     }
  96.     
  97.     switch (policy % 10) {
  98.      case DENY_ACCESS:
  99.     return 2;
  100.  
  101.      case ALL_HOURS:
  102.      case FREE_ACCOUNT:
  103.     break;
  104.  
  105.      case OFF_HOURS:    /* adapt this to your local requirements */
  106.     time(&t);
  107.     tm = localtime(&t);
  108.     if (tm->tm_wday == 0) break;    /* Sunday */
  109.     if (tm->tm_wday == 6) break;    /* Saturday */
  110.     if (tm->tm_hour < 9) break;    /* morning */
  111.     if (tm->tm_hour > 16) break;    /* evening */
  112.     if (holiday(tm)) break;        /* holidays */
  113.     /* etc. */
  114.     return 3;
  115.  
  116.      default:
  117.     return 2;
  118.     }
  119.  
  120.     return no_post;
  121. }
  122.  
  123. #endif
  124.  
  125. static int add_usage = -1;
  126. static int show_cost = -1;
  127. static int report = 0;
  128. static int report_all = 0;
  129. static int quiet = 0;
  130. static char *acct_file = NULL;
  131. static int ck_policy = -1;
  132. static int new_policy = -1;
  133. static int new_quota = -1;
  134. static int who_am_caller = I_AM_ACCT;
  135.  
  136. Option_Description(acct_options) {
  137.     'C', Int_Option(show_cost),
  138.     'U', Int_Option(add_usage),
  139.     'W', Int_Option(who_am_caller),
  140.     'P', Int_Option(ck_policy),
  141.     'a', Bool_Option(report_all),
  142.     'f', String_Option(acct_file),
  143.     'p', Int_Option(new_policy),
  144.     'q', Int_Option(new_quota),
  145.     'r', Bool_Option(report),
  146.     'Q', Bool_Option(quiet),
  147.     '\0',
  148. };
  149.  
  150. /*
  151.  *    Accounting information:
  152.  *
  153.  *    xxxxxxx 00000000 00000000 00 00000\n
  154.  *
  155.  *    login    time used last     pol quota
  156.  *    name    (minutes) active icy (hours)
  157.  *
  158.  *    See the printf/scanf formats later on.
  159.  */
  160.  
  161.  
  162. #define INPUT_FMT    "%s %ld %lx %d %d\n"
  163. #define OUTPUT_FMT    "%s %08ld %08lx %02d %05d\n"
  164.  
  165. struct account {
  166.     off_t ac_offset;    /* offset in acct file */
  167.     int ac_found;    /* present in acct file */
  168.     
  169.     char ac_user[24];    /* user name */
  170.     
  171.     long ac_total;    /* total usage */
  172.     time_t ac_last;    /* last active */
  173.     int ac_policy;    /* assigned policy */
  174.     int ac_quota;    /* time quota */
  175. };
  176.  
  177. static get_entry(acctf, user, ac)
  178. FILE *acctf;
  179. char *user;
  180. struct account *ac;
  181. {
  182.     char line[100];
  183.  
  184.     if (acctf != NULL && user != NULL) 
  185.     rewind(acctf);
  186.     
  187.     ac->ac_found = 0;
  188.  
  189.     for (;;) {
  190.     ac->ac_policy = DEFAULT_POLICY;
  191.     ac->ac_last = 0;
  192.     ac->ac_total = 0;
  193.     ac->ac_quota = DEFAULT_QUOTA;
  194.     ac->ac_user[0] = NUL;
  195.     
  196.     if (acctf == NULL) break;
  197.     
  198.     ac->ac_offset = ftell(acctf);
  199.  
  200.     if (fgets(line, 100, acctf) == NULL) break;
  201.  
  202.     sscanf(line, INPUT_FMT, 
  203.            ac->ac_user, &ac->ac_total, &ac->ac_last, 
  204.            &ac->ac_policy, &ac->ac_quota);
  205.  
  206.     if (user == NULL) return 1;
  207.  
  208.     if (strcmp(user, ac->ac_user) == 0) {
  209.         ac->ac_found = 1;
  210.         return 1;
  211.     }
  212.     }
  213.  
  214.     if (user != NULL) strcpy(ac->ac_user, user);
  215.     return 0;
  216. }
  217.  
  218. put_entry(acctf, ac)
  219. FILE *acctf;
  220. struct account *ac;
  221. {
  222.     if (ac->ac_found)
  223.     fseek(acctf, ac->ac_offset, 0);
  224.     else
  225.     fseek(acctf, (off_t)0, 2);
  226.     
  227.     fprintf(acctf, OUTPUT_FMT, 
  228.         ac->ac_user, ac->ac_total, ac->ac_last,
  229.         ac->ac_policy, ac->ac_quota);
  230. }
  231.  
  232. static do_cost(ac, ses)
  233. struct account *ac;
  234. int ses;
  235. {
  236.     long r;
  237.     
  238. #ifdef COST_PER_MINUTE
  239. #ifndef COST_UNIT
  240. #define COST_UNIT ""
  241. #endif
  242.     if (ses >= 0)
  243.     printf("Cost this session: %ld %s   Period total:",
  244.            ((long)ses * COST_PER_MINUTE) / 100, COST_UNIT);
  245.     printf("%6ld %s",
  246.        (ac->ac_total * COST_PER_MINUTE) / 100, COST_UNIT);
  247. #endif
  248.     if (ses >= 0 && ac->ac_quota > 0) {
  249.     r = ac->ac_quota - ac->ac_total/60;
  250.     printf("  Quota: %ld hour%s", r, plural(r));
  251.     }
  252.     fl;
  253. }
  254.  
  255. static do_report(ac, hdr)
  256. struct account *ac;
  257. int hdr;
  258. {
  259.     if (hdr) {
  260.     printf("USER        USAGE  QUOTA  LAST_ACTIVE");
  261. #ifdef COST_PER_MINUTE
  262.     printf("   COST/PERIOD");
  263. #endif
  264. #ifdef AUTHORIZE
  265.     printf("   POLICY");
  266. #endif
  267.     putchar(NL);
  268.     }
  269.  
  270.     printf("%-8.8s  %4ld.%02ld  %5d  %s  ",
  271.        ac->ac_user,
  272.        ac->ac_total/60, ac->ac_total%60,
  273.        ac->ac_quota,
  274.        ac->ac_last ? date_time(ac->ac_last) : "");
  275. #ifdef COST_PER_MINUTE
  276.     do_cost(ac, -1);
  277. #endif
  278. #ifdef AUTHORIZE
  279.     printf("     %2d  ", ac->ac_policy);
  280. #endif
  281.     printf("\n");
  282. }
  283.  
  284. static do_report_all(acctf)
  285. FILE *acctf;
  286. {
  287.     struct account ac;
  288.     int first = 1;
  289.     
  290.     while (get_entry(acctf, (char *)NULL, &ac)) {
  291.     do_report(&ac, first);
  292.     first = 0;
  293.     }
  294. }
  295.  
  296.  
  297.  
  298. main(argc, argv)
  299. int argc;
  300. char *argv[];
  301. {
  302.     char *caller, *getlogin();
  303.     FILE *acctf;
  304.     char *fname;
  305.     int users, i;
  306.     struct account ac, *actab;
  307.     
  308.     who_am_i = I_AM_ACCT;
  309.  
  310.     init_global();
  311.  
  312.     users = parse_options(argc, argv, (char *)NULL, acct_options, "");
  313.  
  314.     if (user_id != 0) {
  315.     if (report_all) {
  316.         fprintf(stderr, "Only root can request complete reports\n");
  317.         exit(9);
  318.     }
  319.     if (new_policy >= 0) {
  320.         fprintf(stderr, "Only root can change user authorization\n");
  321.         exit(9);
  322.     }
  323.     if (new_quota >= 0) {
  324.         fprintf(stderr, "Only root can change user quotas\n");
  325.         exit(9);
  326.     }
  327.     if (users > 0) {
  328.         fprintf(stderr, "Only root can request reports for other users\n");
  329.         exit(9);
  330.     }
  331.  
  332.     if ((caller = getlogin()) == NULL)
  333.         if ((caller = user_name()) == NULL)
  334.         caller = "UNKNOWN";
  335.     } else
  336.     caller = "root";
  337.  
  338.     if ((new_policy >= 0 || new_quota >= 0) && users == 0) {
  339.     fprintf(stderr, "usage: %s -pPOLICY -qQUOTA user...\n", argv[0]);
  340.     exit(1);
  341.     }
  342.     
  343.     if (add_usage == 0 && report) {
  344.     show_cost = 0;
  345.     add_usage = -1;
  346.     }
  347.  
  348.     if (add_usage > 0 || new_policy >= 0 || new_quota >= 0) {
  349.     if (acct_file) {
  350.         fprintf(stderr, "Can only update current acct file\n", acct_file);
  351.         exit(2);
  352.     }
  353.  
  354.     proto_lock(I_AM_ACCT, PL_SET_QUICK);
  355.     }
  356.  
  357.     if (acct_file) {
  358.     if ((acctf = open_file(acct_file, OPEN_READ)) == NULL)
  359.         acctf = open_file(relative(db_directory, acct_file), OPEN_READ);
  360.     if (acctf == NULL) {
  361.         fprintf(stderr, "Accounting file %s not found\n", acct_file);
  362.         if (add_usage > 0 || new_policy >= 0 || new_quota >= 0)
  363.         proto_lock(I_AM_ACCT, PL_CLEAR);
  364.         exit(1);
  365.     }
  366.     } else {
  367.     fname = relative(db_directory, "acct");
  368.         acctf = open_file(fname, OPEN_READ);
  369.     }
  370.  
  371.     if (report_all) {
  372.     do_report_all(acctf);
  373.     fclose(acctf);
  374.     exit(0);
  375.     }
  376.     
  377.     if (ck_policy >= 0) {
  378. #ifdef AUTHORIZE
  379.     get_entry(acctf, caller, &ac);
  380.     exit(policy_check(ac.ac_policy));
  381. #else
  382.     exit(0);
  383. #endif
  384.     }
  385.     
  386.     if (show_cost >= 0) {
  387.     get_entry(acctf, caller, &ac);
  388.     if (ac.ac_policy == FREE_ACCOUNT) exit(0);
  389.     ac.ac_total += show_cost;
  390.     do_cost(&ac, show_cost);
  391.     exit(0);
  392.     }
  393.     
  394.     if (add_usage > 0) {
  395.     get_entry(acctf, caller, &ac);
  396.     if (ac.ac_policy == FREE_ACCOUNT) goto unlock;
  397.     ac.ac_total += add_usage;
  398.     time(&ac.ac_last);
  399.     } else
  400.     if (users > 0) {
  401.     actab = newobj(struct account, users + 1);
  402.     for (i = 1; i <= users; i++) {
  403.         get_entry(acctf, argv[i], &actab[i]);
  404.         if (new_policy >= 0 || new_quota >= 0) {
  405.         if (new_policy >= 0)
  406.             actab[i].ac_policy = new_policy;
  407.         if (new_quota >= 0)
  408.             actab[i].ac_quota = new_quota;
  409.         } else
  410.         do_report(&actab[i], i == 1);
  411.     }
  412.     } else
  413.     if (report) {
  414.     if (get_entry(acctf, caller, &ac))
  415.         do_report(&ac, 1);
  416.     exit(0);
  417.     }
  418.     
  419.     if (acctf) fclose(acctf);
  420.  
  421.     if (add_usage <= 0 && new_policy < 0 && new_quota < 0) exit(0);
  422.     
  423.     umask(0177);
  424.     acctf = open_file(fname, OPEN_UPDATE | MUST_EXIST);
  425.     
  426.     if (new_policy >= 0 || new_quota >= 0) {
  427.     for (i = 1; i <= users; i++)
  428.         put_entry(acctf, &actab[i]);
  429.     fclose(acctf);
  430.     goto unlock;
  431.     }
  432.  
  433.     if (add_usage > 0) {
  434.     put_entry(acctf, &ac);
  435.     if (report) {
  436. #ifdef COST_PER_MINUTE
  437.         do_cost(&ac, add_usage);
  438. #else
  439.         strcpy(ac.ac_user, "Total:");
  440.         do_report(&ac, 0);
  441. #endif
  442.     }
  443.     fclose(acctf);
  444. #ifdef ACCTLOG
  445.     fname = relative(db_directory, "acctlog");
  446.         acctf = open_file(fname, OPEN_APPEND | MUST_EXIST);
  447.     fprintf(acctf, "%s\t%s\t%ld\n",
  448.         caller, date_time(ac.ac_last), (long)add_usage);
  449.     fclose(acctf);
  450. #endif
  451.     goto unlock;
  452.     }
  453.  
  454.  unlock:
  455.     proto_lock(I_AM_ACCT, PL_CLEAR);
  456.     exit(0);
  457.     /*NOTREACHED*/
  458. }
  459.  
  460. nn_exit(n)
  461. {
  462.     exit(n);
  463. }
  464.  
  465. user_error()
  466. {
  467.     exit(2);
  468. }
  469.  
  470. #ifdef HAVE_JOBCONTROL
  471. suspend_nn()
  472. {}
  473. #endif
  474.