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

  1. /*
  2.  *    (c) Copyright 1990, Kim Fabricius Storm.  All rights reserved.
  3.  *
  4.  *    nnadmin - nn administator program
  5.  */
  6.  
  7. #include <signal.h>
  8. #include <errno.h>
  9. #include "config.h"
  10. #include "db.h"
  11. #include "proto.h"
  12. #include "term.h"
  13.  
  14. import char
  15.     *master_directory, *db_directory, *db_data_directory,
  16.     *bin_directory, *news_directory, *news_lib_directory,
  17.     *log_file, *news_active, *pager,
  18.     group_path_name[];
  19.  
  20. import char *exec_chdir_to;
  21.  
  22.  
  23. static char *pre_input;
  24. static int verbose = 1;
  25.  
  26. extern int group_completion();
  27.  
  28.  
  29. static get_cmd(prompt1, prompt2)
  30. char *prompt1, *prompt2;
  31. {
  32.     char c;
  33.  
  34.     if (s_hangup) {
  35.     printf("\nnnmaster hangup\n");
  36.     nn_exit(0);
  37.     }
  38.  
  39.     s_keyboard = 0;
  40.  
  41.     if (pre_input) {
  42.     if ((c = *pre_input++) == NUL)
  43.         nn_exit(0);
  44.     } else {
  45.     do {
  46.         if (prompt1) printf("\r%s\n", prompt1);
  47.         if (prompt2) printf("\r%s >>>", prompt2);
  48.         fl;
  49.         raw();
  50.         c = get_c();
  51.         unset_raw();
  52.         if (c == K_interrupt)
  53.         s_keyboard++;
  54.         else
  55.         if (c == '!') {
  56.         putchar(NL);
  57.         if (exec_chdir_to != NULL)
  58.             printf("\n\rDirectory: %s", exec_chdir_to);
  59.         run_shell((char *)NULL, 0);
  60.         } else
  61.         printf("%c\n\n\r", c);
  62.     } while (c == '!');
  63.     }
  64.  
  65.     if (islower(c))
  66.     c = toupper(c);
  67.  
  68.     return c;
  69. }
  70.  
  71.  
  72. static long get_entry(prompt_str, min_val, max_val)
  73. char *prompt_str;
  74. long min_val, max_val;
  75. {
  76.     char buf[100];
  77.     long val;
  78.  
  79.  loop:
  80.  
  81.     printf("%s %ld..%ld (or all): ", prompt_str, min_val, max_val);
  82.     fl;
  83.     gets(buf);
  84.     if (buf[0] == 'a' || buf[0] == NUL)
  85.     return -1L;
  86.  
  87.     val =  atol(buf);
  88.     if (val < min_val || val > max_val) goto loop;
  89.  
  90.     return val;
  91. }
  92.  
  93.  
  94. static admin_confirm(action, must_confirm)
  95. char *action;
  96. {
  97.     char buffer[100];
  98.  
  99.     if (pre_input && !must_confirm) return 1;
  100.  
  101.     sprintf(buffer, "Confirm %s  Y)es N)o", action);
  102.  
  103.     return get_cmd((char *)NULL, buffer) == 'Y';
  104. }
  105.  
  106. static char *get_groupname()
  107. {
  108.     char * groupname;
  109.  
  110.     raw();
  111.     printf("\n\n\r");
  112.     prompt_line = Lines - 2;
  113.     prompt("Group: ");
  114.     fl;
  115.     groupname = get_s(NONE, NONE, NONE, group_completion);
  116.     unset_raw();
  117.  
  118.     putchar(NL); putchar(CR);
  119.  
  120.     if (groupname == NULL) return NULL;
  121.  
  122.     if (groupname[0]) return groupname;
  123.  
  124.     if (current_group == NULL) return NULL;
  125.     if (current_group->group_flag & G_FOLDER) return NULL;
  126.  
  127.     return current_group->group_name;
  128. }
  129.  
  130.  
  131. static update_master()
  132. {
  133.     register group_header *gh;
  134.     int ngp;
  135.  
  136.     if (who_am_i != I_AM_ADMIN) {
  137.     printf("Can only perform (U)pdate as nnadmin\n");
  138.     return;
  139.     }
  140.  
  141.     ngp = master.number_of_groups;
  142.  
  143.     open_master(OPEN_READ);
  144.  
  145.     if (master.number_of_groups != ngp) {
  146.     printf("\nNumber of groups changed from %d to %d\n",
  147.            ngp, master.number_of_groups);
  148.     }
  149.  
  150.     ngp = 0;
  151.  
  152.     Loop_Groups_Header(gh)
  153.     if (update_group(gh) == -1) ngp++;
  154.  
  155.     if (ngp) printf("There are %d blocked groups\n", ngp);
  156. }
  157.  
  158. static find_files(gh)
  159. group_header *gh;
  160. {
  161.     char command[512], name[FILENAME], *db_data_path();
  162.  
  163.     if (gh == NULL) {
  164.     if (db_data_directory == NULL) {
  165.         printf("Cannot list all files (they are scattered over the news partition)\n");
  166.         return;
  167.     }
  168.     sprintf(command, "ls -l %s | %s", db_data_directory, pager);
  169.     } else
  170.     sprintf(command, "ls -l %s", db_data_path(name, gh, '*'));
  171.     system(command);
  172. }
  173.  
  174. static master_status()
  175. {
  176.     int cur_group, nblocked, nignored;
  177.     long articles, disk_use;
  178.     register group_header *gh;
  179.  
  180.     printf("\nMaster:\n");
  181.     printf("   initialized:   %s\n", date_time(master.db_created));
  182.     printf("   last_scan:     %s\n", date_time(master.last_scan));
  183.     printf("   last_size:     %ld\n", (long)master.last_size);
  184.     printf("   no of groups:  %d\n", master.number_of_groups);
  185.  
  186.     articles = disk_use = 0;
  187.     nblocked = nignored = 0;
  188.  
  189.     for (cur_group = 0; cur_group < master.number_of_groups; cur_group++) {
  190.     gh = &active_groups[cur_group];
  191.  
  192. #define DISK_BLOCKS(bytes) (((bytes) + 1023) / 1024)
  193.  
  194.     disk_use += DISK_BLOCKS(gh->index_write_offset);
  195.     disk_use += DISK_BLOCKS(gh->data_write_offset);
  196.  
  197.     articles += gh->last_db_article - gh->first_db_article + 1;
  198.  
  199.     if (gh->master_flag & M_BLOCKED) nblocked++;
  200.     if (gh->master_flag & M_IGNORE_GROUP) nignored++;
  201.     }
  202.  
  203.     printf("\n   Articles:   %ld\n", articles);
  204.     printf(  "   Disk usage: %ld k\n\n", disk_use);
  205.  
  206.     if (nblocked) printf("Blocked groups: %3d\n", nblocked);
  207.     if (nignored) printf("Ignored groups: %3d\n", nignored);
  208. }
  209.  
  210. static dump_g_flag(gh)
  211. register group_header *gh;
  212. {
  213.     printf("Flags: ");
  214.     if (gh->master_flag & M_BLOCKED)     printf(" BLOCKED");
  215.     if (gh->master_flag & M_EXPIRE)     printf(" EXPIRE");
  216.     if (gh->master_flag & M_MODERATED)     printf(" MODERATED");
  217.     if (gh->master_flag & M_CONTROL)     printf(" CONTROL");
  218.     if (gh->master_flag & M_NO_DIRECTORY) printf(" NO_DIRECTORY");
  219.     if (gh->master_flag & M_ALWAYS_DIGEST) printf(" ALWAYS_DIGEST");
  220.     if (gh->master_flag & M_NEVER_DIGEST) printf(" NEVER_DIGEST");
  221.     if (gh->master_flag & M_INCLUDE_OLD) printf(" INCL_OLD");
  222.     if (gh->master_flag & M_AUTO_RECOLLECT) printf(" RECOLLECT");
  223.     if (gh->master_flag & M_AUTO_ARCHIVE) printf(" ARCHIVE");
  224.     if (gh->master_flag & M_IGNORE_GROUP) printf(" IGNORE");
  225.     if (gh->master_flag & M_VALID) printf(" VALID");
  226.     printf("\n");
  227. }
  228.  
  229. static dump_m_entry(gh)
  230. register group_header *gh;
  231. {
  232.     update_group(gh);
  233.  
  234.     printf("\n%s\t%d\n", gh->group_name, gh->group_num);
  235.     printf("first/last art: %06ld %06d\n",
  236.        gh->first_db_article, gh->last_db_article);
  237.     printf("   active info: %06ld %06d\n",
  238.        gh->first_a_article, gh->last_a_article);
  239.     printf("Offsets: index->%ld, data->%ld\n",
  240.        gh->index_write_offset,
  241.        gh->data_write_offset);
  242.     if (gh->master_flag & M_AUTO_ARCHIVE)
  243.     printf("Archive file: %s\n", gh->archive_file);
  244.     if (gh->master_flag)
  245.     dump_g_flag(gh);
  246. }
  247.  
  248. #define valerr( xxx , tp) { \
  249.     if (verbose) { printf xxx ; fl; } \
  250.     err_type = tp; \
  251.     goto err; \
  252. }
  253.  
  254. static validate_group(gh)
  255. group_header *gh;
  256. {
  257.     FILE *data, *ix;
  258.     off_t data_offset, next_offset;
  259.     cross_post_number cross_post;
  260.     article_number cur_article;
  261.     int n, err_type;
  262.  
  263.     data = ix = NULL;
  264.  
  265.     if (gh->first_db_article == (gh->last_db_article + 1)
  266.     && gh->index_write_offset == 0)
  267.     return 1;
  268.  
  269.     if (verbose) { printf("\r%s: ", gh->group_name); clrline(); }
  270.  
  271.     if (init_group(gh) <= 0) {
  272.     printf("NO DIRECTORY (ok)");
  273.     return 1; /* no directory/ignored */
  274.     }
  275.  
  276.     update_group(gh);
  277.  
  278.     if (gh->master_flag & M_BLOCKED) {
  279.     if (verbose) printf("BLOCKED (ok)\n");
  280.     return 1;
  281.     }
  282.  
  283.     /*
  284.      *    Check for major inconcistencies.
  285.      *    Sometimes, news expire will reset article numbers
  286.      *    to start from 0 again
  287.      */
  288.  
  289.     if (gh->last_db_article == 0) {
  290.     return 1;
  291.     }
  292.  
  293. #ifdef RENUMBER_DANGER
  294.     if (gh->first_a_article > (gh->last_db_article + 1) ||
  295.     gh->last_db_article  > gh->last_a_article ||
  296.     gh->first_db_article > gh->first_a_article) {
  297.  
  298.     if (verbose)
  299.         printf("RENUMBERING OF ARTICLES (active=%ld..%ld master=%ld..%ld)",
  300.            gh->first_a_article, gh->last_a_article,
  301.            gh->first_db_article, gh->last_db_article);
  302.     err_type = 99;
  303.     goto err;
  304.     }
  305. #endif
  306.  
  307.     ix = open_data_file(gh, 'x', OPEN_READ);
  308.     if (ix == NULL) valerr(("NO INDEX FILE"), 1);
  309.  
  310.     data = open_data_file(gh, 'd', OPEN_READ);
  311.     if (data == NULL) valerr(("NO DATA FILE"), 2);
  312.  
  313.     cur_article = gh->first_db_article - 1;
  314.  
  315.     while (cur_article <= gh->last_db_article) {
  316.     if (s_hangup || s_keyboard) goto out;
  317.  
  318.     data_offset = ftell(data);
  319.  
  320.     switch (db_read_art(data)) {
  321.      case 0:
  322.         if (data_offset == gh->data_write_offset) goto out;
  323.         valerr(("No header for article # %ld", (long)cur_article+1), 3);
  324.      case 1:
  325.         break;
  326.      case -1:
  327.         valerr(("END OF FILE on DATA FILE"), 4);
  328.     }
  329.  
  330.     if (db_hdr.dh_number <= 0 || cur_article > db_hdr.dh_number)
  331.         valerr(("OUT OF SEQUENCE: %ld after %ld", (long)db_hdr.dh_number, (long)cur_article), 11);
  332.  
  333.     if (cur_article < db_hdr.dh_number) {
  334.         if (db_data.dh_type == DH_SUB_DIGEST)
  335.         valerr(("NO DIGEST HEADER: %ld", (long)db_hdr.dh_number), 5);
  336.  
  337.         do {
  338.         cur_article++;
  339.         if (!db_read_offset(ix, &next_offset))
  340.             valerr(("NO INDEX FOR ARTICLE %ld", (long)cur_article), 6);
  341.  
  342.         if (data_offset != next_offset)
  343.             valerr(("OFFSET ERROR: %ld: %ld != %ld", (long)cur_article, (long)data_offset, (long)next_offset), 7);
  344.         } while (cur_article < db_hdr.dh_number);
  345.     }
  346.  
  347.     for (n = 0; n < db_hdr.dh_cross_postings; n++) {
  348.         cross_post = NETW_CROSS_INT(db_data.dh_cross[n]);
  349.         if (cross_post < master.number_of_groups) continue;
  350.         valerr(("CROSS POST RANGE ERROR: %ld (article # %ld)", (long)cross_post, (long)cur_article), 8);
  351.     }
  352.     }
  353.  
  354.  out:
  355.     if (!s_keyboard && !s_hangup) {
  356.     data_offset = ftell(data);
  357.     if (data_offset != gh->data_write_offset)
  358.         valerr(("DATA OFFSET %ld != %ld", (long)gh->data_write_offset, (long)data_offset), 9);
  359.     data_offset = ftell(ix);
  360.     if (data_offset != gh->index_write_offset)
  361.         valerr(("INDEX OFFSET %ld != %ld", (long)gh->index_write_offset, (long)data_offset), 10);
  362.     }
  363.  
  364.     fclose(data);
  365.     fclose(ix);
  366.     if (verbose) printf("OK");
  367.     return 1;
  368.  
  369.  err:
  370.     if (data != NULL) fclose(data);
  371.     if (ix != NULL) fclose(ix);
  372.     log_entry('V', "%s: database error %d", gh->group_name, err_type);
  373.     if (verbose) {
  374.     putchar(NL);
  375.     dump_m_entry(gh);
  376.     }
  377.  
  378.     if (!pre_input) {
  379.     ding();
  380.  
  381.     for (;;) {
  382.         switch (get_cmd((char *)NULL,
  383. "\nRepair group   Y)es N)o E)nter Q)uit")) {
  384.          case 'Y':
  385.         break;
  386.          case 'N':
  387.         return 0;
  388.          case 'Q':
  389.         s_keyboard++;
  390.         return 0;
  391.          case 'E':
  392.         group_admin(gh);
  393.         continue;
  394.          default:
  395.         continue;
  396.         }
  397.         break;
  398.     }
  399.     }
  400.  
  401.     send_master(SM_RECOLLECT, gh, SP, 0L);
  402.     return 0;
  403. }
  404.  
  405. static dump_group(gh)
  406. group_header *gh;
  407. {
  408.     FILE *data, *ix;
  409.     off_t offset;
  410.     cross_post_number cross_post;
  411.     article_number first_article;
  412.     int n;
  413.  
  414.     if (init_group(gh) <= 0)
  415.     printf("cannot access group %s\n", gh->group_name);
  416.  
  417.     update_group(gh);
  418.  
  419.     first_article = get_entry("First article",
  420.                   (long)gh->first_db_article,
  421.                   (long)gh->last_db_article);
  422.  
  423.     if (first_article < 0) first_article = gh->first_db_article;
  424.     if (first_article <= 0) first_article = 1;
  425.  
  426.     ix = open_data_file(gh, 'x', OPEN_READ);
  427.     data = open_data_file(gh, 'd', OPEN_READ);
  428.     if (ix == NULL || data == NULL) goto err;
  429.  
  430.     fseek(ix, get_index_offset(gh, first_article), 0);
  431.     if (!db_read_offset(ix, &offset)) goto err;
  432.     fseek(data, offset, 0);
  433.  
  434.     clrdisp();
  435.     pg_init(1, 1);
  436.  
  437.     for (;;) {
  438.     if (s_hangup || s_keyboard) break;
  439.  
  440.     if (pg_scroll(6)) {
  441.         s_keyboard = 1;
  442.         break;
  443.     }
  444.  
  445.     offset = ftell(data);
  446.  
  447.     switch (db_read_art(data)) {
  448.      case 0:
  449.         goto out;
  450.      case 1:
  451.         break;
  452.      case -1:
  453.         goto err;
  454.     }
  455.  
  456.     printf("\noffset = %ld, article # = %ld",
  457.            (long)offset, (long)(db_hdr.dh_number));
  458.  
  459.     switch (db_data.dh_type) {
  460.      case DH_DIGEST_HEADER:
  461.         printf(" (digest header)\n");
  462.         break;
  463.      case DH_SUB_DIGEST:
  464.         printf(" (digest sub-article)\n");
  465.         break;
  466.      case DH_NORMAL:
  467.         putchar(NL);
  468.         break;
  469.     }
  470.  
  471.     if (db_hdr.dh_cross_postings) {
  472.         printf("xpost(%d):", db_hdr.dh_cross_postings);
  473.  
  474.         for (n = 0; n < db_hdr.dh_cross_postings; n++) {
  475.         cross_post = NETW_CROSS_INT(db_data.dh_cross[n]);
  476.         printf(" %d", cross_post);
  477.         }
  478.         putchar(NL);
  479.     }
  480.  
  481.     printf("ts=%lu hp=%ld, fp=+%d, lp=%ld, ref=%d%s, lines=%d\n",
  482.            (long unsigned)db_hdr.dh_date, (long)db_hdr.dh_hpos,
  483.            (int)db_hdr.dh_fpos, (long)db_hdr.dh_lpos,
  484.            db_hdr.dh_replies & 0x7f,
  485.            (db_hdr.dh_replies & 0x80) ? "+Re" : "",
  486.            db_hdr.dh_lines);
  487.  
  488.     if (db_hdr.dh_sender_length)
  489.         printf("Sender(%d): %s\n", db_hdr.dh_sender_length, db_data.dh_sender);
  490.     else
  491.         printf("No sender\n");
  492.  
  493.     if (db_hdr.dh_subject_length)
  494.         printf("Subj(%d): %s\n", db_hdr.dh_subject_length, db_data.dh_subject);
  495.     else
  496.         printf("No subject\n");
  497.     }
  498.  
  499.  out:
  500.     if (!s_keyboard && !s_hangup && ftell(data) != gh->data_write_offset)
  501.     goto err;
  502.  
  503.     fclose(data);
  504.     fclose(ix);
  505.     return 1;
  506.  
  507.  err:
  508.     if (data != NULL) fclose(data);
  509.     if (ix != NULL) fclose(ix);
  510.     printf("\n*** DATABASE INCONSISTENCY DETECTED - run V)alidate\n");
  511.     return 0;
  512. }
  513.  
  514. static kill_master(term)
  515. int term;
  516. {
  517.     switch (proto_lock(I_AM_MASTER, term ? PL_TERMINATE : PL_WAKEUP_SOFT)) {
  518.      case 1:
  519.     if (verbose)
  520.         printf("sent %s signal to master\n", term ? "termination" : "wakeup");
  521.     break;
  522.      case 0:
  523.     printf("cannot signal master\n");
  524.     break;
  525.      case -1:
  526.     if (verbose) printf("master is not running\n");
  527.     break;
  528.     }
  529. }
  530.  
  531. static master_admin()
  532. {
  533.     register char c;
  534.     int cur_group;
  535.     long value;
  536.     register group_header *gh;
  537.  
  538.     exec_chdir_to = db_directory;
  539.  
  540.     for (;;) {
  541.     switch (c = get_cmd(
  542. "\nD)ump F)iles G)roup K)ill O)ptions S)tat T)race",
  543. "MASTER")) {
  544.  
  545.      case 'G':
  546.         cur_group = (int)get_entry("Group number",
  547.                   0L, (long)(master.number_of_groups - 1));
  548.         if (cur_group >= 0)
  549.         dump_m_entry(&active_groups[cur_group]);
  550.         break;
  551.  
  552.      case 'D':
  553.         do {
  554.         c = get_cmd(
  555. "\nA)ll B)locked E)mpty H)oles I)gnored N)on-empty V)alid in(W)alid",
  556. "DUMP");
  557.         cur_group = -1;
  558.         pg_init(1, 1);
  559.  
  560.         while (++cur_group < master.number_of_groups) {
  561.             if (s_keyboard || s_hangup) break;
  562.  
  563.             gh = &active_groups[cur_group];
  564. #define NODUMP(cond) if (cond) continue; break
  565.             switch (c) {
  566.              case 'N':
  567.             NODUMP(gh->index_write_offset == 0);
  568.              case 'E':
  569.             NODUMP(gh->index_write_offset != 0);
  570.              case 'H':
  571.             NODUMP(gh->first_a_article >= gh->first_db_article);
  572.              case 'B':
  573.             NODUMP((gh->master_flag & M_BLOCKED) == 0);
  574.              case 'I':
  575.             NODUMP((gh->master_flag & M_IGNORE_GROUP) == 0);
  576.              case 'V':
  577.             NODUMP((gh->master_flag & M_VALID) == 0);
  578.              case 'W':
  579.             NODUMP(gh->master_flag & M_VALID);
  580.              default:
  581.             s_keyboard = 1; continue;
  582.             }
  583.  
  584.             if (pg_scroll(6)) break;
  585.             dump_m_entry(gh);
  586.         }
  587.         } while (!s_keyboard && !s_hangup);
  588.         break;
  589.  
  590.      case 'F':
  591.         find_files((group_header *)NULL);
  592.         break;
  593.  
  594.      case 'O':
  595.         c = get_cmd("r)epeat_delay  e)xpire_level", "OPTION");
  596.         if (c != 'R' && c != 'E') break;
  597.         value = get_entry("Option value", 1L, 10000L);
  598.         if (value < 0) break;
  599.         send_master(SM_SET_OPTION, (group_header *)NULL, tolower(c), value);
  600.         break;
  601.  
  602.      case 'S':
  603.         master_status();
  604.         break;
  605.  
  606.      case 'T':
  607.         send_master(SM_SET_OPTION, (group_header *)NULL, 't', -1L);
  608.         break;
  609.  
  610.      case 'K':
  611.         if (admin_confirm("Stop nn Master", 0))
  612.         kill_master(1);
  613.         break;
  614.  
  615.      default:
  616.         exec_chdir_to = NULL;
  617.         return;
  618.     }
  619.     }
  620. }
  621.  
  622.  
  623. static log_admin()
  624. {
  625.     char command[FILENAME + 100], c;
  626.  
  627.     if (pre_input && *pre_input == NUL) {
  628.     c = SP;
  629.     goto log_tail;
  630.     }
  631.  
  632.  loop:
  633.  
  634.     c = get_cmd(
  635. "\n1-9)tail A)dm C)ollect E)rr N)ntp G)roup R)eport e(X)pire .)all @)clean",
  636. "LOG");
  637.  
  638.     if (c < SP || c == 'Q') return;
  639.  
  640.     if (c == '@') {
  641.     if (admin_confirm("Truncation", 0)) {
  642.         sprintf(command, "%s.old", log_file);
  643.         unlink(command);
  644.         if (link(log_file, command) < 0) goto tr_failed;
  645.         if (unlink(log_file) < 0) {
  646.         unlink(command);
  647.         goto tr_failed;
  648.         }
  649.         log_entry('A', "Log Truncated");
  650.         chmod(log_file, 0666);
  651.     }
  652.     return;
  653.  
  654.      tr_failed:
  655.     printf("Truncation failed -- check permissions etc.\n");
  656.     goto loop;
  657.     }
  658.  
  659.     if (c == 'G') {
  660.     char *groupname;
  661.  
  662.     if ((groupname = get_groupname()) == NULL) goto loop;
  663.     sprintf(command, "fgrep '%s' %s | %s",
  664.         groupname, log_file, pager);
  665.     system(command);
  666.  
  667.     goto loop;
  668.     }
  669.  
  670.  log_tail:
  671.     if (c == '$' || c == SP || isdigit(c)) {
  672.     int n;
  673.  
  674.     n = isdigit(c) ? 10 * (c - '0') : 10;
  675.     sprintf(command, "tail -%d %s", n, log_file);
  676.     system(command);
  677.     goto loop;
  678.     }
  679.  
  680.     if (c == '*') {
  681.     c = '.';
  682.     }
  683.  
  684.     sprintf(command, "grep '^%c:' %s | %s", c, log_file, pager);
  685.     system(command);
  686.  
  687.     goto loop;
  688. }
  689.  
  690.  
  691. static flag_admin(gh, mode_str, set_flag)
  692. group_header *gh;
  693. char *mode_str;
  694. int set_flag;
  695. {
  696.     char buffer[50];
  697.     int new_flag = 0;
  698.  
  699.     putchar(NL);
  700.  
  701.     dump_g_flag(gh);
  702.  
  703.     sprintf(buffer, "%s FLAG", mode_str);
  704.  
  705.     switch (get_cmd(
  706. "\nA)lways_digest N)ever_digest M)oderated C)ontrol no_(D)ir",
  707. buffer)) {
  708.  
  709.      default:
  710.     return;
  711.  
  712.      case 'M':
  713.     new_flag = M_MODERATED;
  714.     break;
  715.  
  716.      case 'C':
  717.     new_flag = M_CONTROL;
  718.     break;
  719.  
  720.      case 'D':
  721.     new_flag = M_NO_DIRECTORY;
  722.     break;
  723.  
  724.      case 'A':
  725.     new_flag = M_ALWAYS_DIGEST;
  726.     break;
  727.  
  728.      case 'N':
  729.     new_flag = M_NEVER_DIGEST;
  730.     break;
  731.     }
  732.  
  733.     if (new_flag & (M_CONTROL | M_NO_DIRECTORY))
  734.     if (!admin_confirm("Flag Change", 0))
  735.         new_flag = 0;
  736.  
  737.     if (new_flag) {
  738.     if (set_flag) {
  739.         if (gh->master_flag & new_flag)
  740.         new_flag = 0;
  741.         else {
  742.         send_master(SM_SET_FLAG, gh, 's', (long)new_flag);
  743.         gh->master_flag |= new_flag;
  744.         }
  745.     } else {
  746.         if ((gh->master_flag & new_flag) == 0)
  747.         new_flag = 0;
  748.         else {
  749.         send_master(SM_SET_FLAG, gh, 'c', (long)new_flag);
  750.         gh->master_flag &= ~new_flag;
  751.         }
  752.     }
  753.     }
  754.  
  755.     if (new_flag == 0)
  756.     printf("NO CHANGE\n");
  757.     else
  758.     dump_g_flag(gh);
  759. }
  760.  
  761.  
  762. static rmgroup(gh)
  763. group_header *gh;
  764. {
  765.     char command[FILENAME*2];
  766.  
  767.     if (!file_exist(news_active, "w")) {
  768.     printf("Not privileged to run rmgroup\n");
  769.     return;
  770.     }
  771.  
  772.     sprintf(command, "%s/rmgroup %s", news_lib_directory, gh->group_name);
  773.     system(command);
  774.     any_key(0);
  775.     gh->master_flag &= ~M_VALID;        /* just for nnadmin */
  776.     gh->master_flag |= M_IGNORE_A;
  777. }
  778.  
  779. static group_admin(gh)
  780. register group_header *gh;
  781. {
  782.     char *groupname, gbuf[FILENAME], dirbuf[FILENAME];
  783.  
  784.     if (gh != NULL) goto have_group;
  785.  
  786.  new_group:
  787.     if ((groupname = get_groupname()) == NULL) return;
  788.  
  789.     gh = lookup(groupname);
  790.     if (gh == NULL) {
  791.     printf("No group named %s\n", groupname);
  792.     goto new_group;
  793.     }
  794.  
  795.  have_group:
  796.     if (!use_nntp && init_group(gh)) {
  797.     strcpy(dirbuf, group_path_name);
  798.     dirbuf[strlen(dirbuf)-1] = NUL;
  799.     exec_chdir_to = dirbuf;
  800.     }
  801.  
  802.     sprintf(gbuf, "GROUP %s", gh->group_name);
  803.  
  804.     for (;;) {
  805.     switch (get_cmd(
  806. "\nD)ata E)xpire F)iles G)roup H)eader R)ecollect V)alidate Z)ap",
  807. gbuf)) {
  808.      case 'D':
  809.         dump_group(gh);
  810.         break;
  811.  
  812.      case 'V':
  813.         verbose = 1;
  814.         validate_group(gh);
  815.         putchar(NL);
  816.         break;
  817.  
  818.      case 'H':
  819.         dump_m_entry(gh);
  820.         break;
  821.  
  822.      case 'F':
  823.         find_files(gh);
  824.         break;
  825.  
  826.      case 'S':
  827.         flag_admin(gh, "Set", 1);
  828.         break;
  829.  
  830.      case 'C':
  831.         flag_admin(gh, "Clear", 0);
  832.         break;
  833.  
  834.      case 'R':
  835.         if (admin_confirm("Recolletion of Group", 0))
  836.         send_master(SM_RECOLLECT, gh, SP, 0L);
  837.         break;
  838.  
  839.      case 'Z':
  840.         if (admin_confirm("Remove Group (run rmgroup)", 1))
  841.         rmgroup(gh);
  842.         break;
  843.  
  844.      case 'E':
  845.         if (admin_confirm("Expire Group", 0))
  846.         send_master(SM_EXPIRE, gh, SP, 0L);
  847.         break;
  848.  
  849.      case 'G':
  850.         goto new_group;
  851.  
  852.      default:
  853.         exec_chdir_to = NULL;
  854.         return;
  855.     }
  856.     }
  857. }
  858.  
  859.  
  860. static show_config()
  861. {
  862. #ifdef NNTP
  863.     extern char nntp_server[];
  864. #endif
  865.  
  866.     printf("\nConfiguration:\n\n");
  867.     printf("BIN:  %s\nLIB:  %s\nNEWS SPOOL: %s\nNEWS LIB: %s\n",
  868.        bin_directory,
  869.        lib_directory,
  870.        news_directory,
  871.        news_lib_directory);
  872.  
  873.     printf("DB:   %s  %s\n", db_directory,
  874.        db_data_directory != NULL ? db_data_directory :
  875.        "(per-group files in group directories)");
  876.  
  877.     printf("ACTIVE: %s\n", news_active);
  878.  
  879. #ifdef NNTP
  880.     if (use_nntp)
  881.     printf("NNTP ACTIVE. server=%s\n", nntp_server);
  882.     else
  883.     printf("NNTP NOT ACTIVE\n");
  884. #endif
  885.  
  886. #ifdef NETWORK_DATABASE
  887.     printf("Database is machine independent (network format).\n");
  888. #ifdef NETWORK_BYTE_ORDER
  889.     printf("Local system assumes to use network byte order\n");
  890. #endif
  891. #else
  892.     printf("Database format is machine dependent (byte order and alignment)\n");
  893. #endif
  894.     printf("Database version: %d\n", NNDB_MAGIC & 0xff);
  895.  
  896. #ifdef STATISTICS
  897.     printf("Recording usage statistics\n");
  898. #else
  899.     printf("No usage statistics are recorded\n");
  900. #endif
  901.  
  902.     printf("Mail delivery program: %s\n", REC_MAIL);
  903. #ifdef APPEND_SIGNATURE
  904.     printf("Query for appending .signature ENABLED\n");
  905. #else
  906.     printf("Query for appending .signature DISABLED\n");
  907. #endif
  908.  
  909.     printf("Max pathname length is %d bytes\n", FILENAME-1);
  910. }
  911.  
  912.  
  913. admin_mode(input_string)
  914. char *input_string;
  915. {
  916.     register group_header *gh;
  917.     int was_raw = unset_raw();
  918.  
  919.     if (input_string && *input_string) {
  920.     pre_input = input_string;
  921.     } else {
  922.     pre_input = NULL;
  923.     printf("\nMaster is%s running\n",
  924.            proto_lock(I_AM_MASTER, PL_CHECK) < 0 ? " NOT" : "");
  925.     }
  926.  
  927.     for (;;) {
  928.     switch(get_cmd(
  929. "\nC)onf E)xpire G)roups I)nit L)og M)aster Q)uit S)tat U)pdate V)alidate W)akeup",
  930. "ADMIN")) {
  931.  
  932.      case 'M':
  933.         master_admin();
  934.         break;
  935.  
  936.      case 'W':
  937.         kill_master(0);
  938.         break;
  939.  
  940.      case 'E':
  941.         if (admin_confirm("Expire All Groups", 1))
  942.         send_master(SM_EXPIRE, (group_header *)NULL, SP, 0L);
  943.         break;
  944.  
  945.      case 'I':
  946.         if (admin_confirm("INITIALIZATION, i.e. recollect all groups", 1))
  947.         send_master(SM_RECOLLECT, (group_header *)NULL, SP, 0L);
  948.         break;
  949.  
  950.      case 'G':
  951.         group_admin((group_header *)NULL);
  952.         break;
  953.  
  954.      case 'L':
  955.         log_admin();
  956.         break;
  957.  
  958.      case 'S':
  959.         master_status();
  960.         break;
  961.  
  962.      case 'C':
  963.         show_config();
  964.         break;
  965.  
  966.      case 'U':
  967.         update_master();
  968.         break;
  969.  
  970.      case 'Z':
  971.         verbose = 0;
  972.         /* FALL THRU */
  973.  
  974.      case 'V':
  975.         Loop_Groups_Sorted(gh) {
  976.         if (s_hangup || s_keyboard) break;
  977.         validate_group(gh);
  978.         }
  979.         verbose = 1;
  980.         break;
  981.  
  982.      case '=':
  983.         verbose = !verbose;
  984.         break;
  985.  
  986.      case 'Q':
  987.         if (was_raw) raw();
  988.         return;
  989.     }
  990.     }
  991. }
  992.