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