home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume25 / trn / part10 / mthreads.c next >
Encoding:
C/C++ Source or Header  |  1991-12-02  |  33.1 KB  |  1,464 lines

  1. /* $Id: mthreads.c,v 4.4.3.1 1991/11/22 04:12:15 davison Trn $
  2. **
  3. ** $Log: mthreads.c,v $
  4. ** Revision 4.4.3.1  1991/11/22  04:12:15  davison
  5. ** Trn Release 2.0
  6. ** 
  7. */
  8.  
  9. /* mthreads.c -- for making and updating a discussion-thread database
  10. **
  11. ** We use the active file to read the high/low counts for each newsgroup
  12. ** and compare them with the corresponding values in a file called active2
  13. ** (which we created to keep the database high/lows in one place).  If they
  14. ** don't match, we read/update/write the group's thread file and move on.
  15. ** If the active2 file is missing or corrupted, it will be repaired in the
  16. ** normal course of operation.
  17. **
  18. ** Usage:  mthreads [-d[MM]] [-e[HHMM]] [-s[hsec]] [-aDfknv] [hierarchy_list]
  19. */
  20.  
  21. #include "EXTERN.h"
  22. #include "common.h"
  23. #include "threads.h"
  24. #ifdef SERVER
  25. #include "server.h"
  26. #endif
  27. #include "INTERN.h"
  28. #include "mthreads.h"
  29.  
  30. #ifdef TZSET
  31. #include <time.h>
  32. #else
  33. #include <sys/time.h>
  34. #include <sys/timeb.h>
  35. #endif
  36.  
  37. #if !defined(FTRUNCATE) && !defined(CHSIZE)
  38. # ifdef F_FREESP
  39. #  define MYCHSIZE
  40. # else
  41. #  define MVTRUNC
  42. # endif
  43. #endif
  44.  
  45. #ifdef USESYSLOG
  46. #include <syslog.h>
  47. #else
  48. FILE *fp_log;
  49. #endif
  50.  
  51. FILE *fp_tmp, *fp_active, *fp_active2, *fp_active2w = Nullfp;
  52. bool eof_active = FALSE, eof_active2 = FALSE;
  53. long first, last, first2, last2;
  54. char ch, ch2;
  55.  
  56. struct stat filestat;
  57.  
  58. char buf[LBUFLEN+1];
  59.  
  60. static char line[256];
  61. static char line2[256];
  62.  
  63. char fmt_active2[] = "%s %010ld %07ld %c\n";
  64.  
  65. char *filename;
  66.  
  67. typedef struct _active_line {
  68.     struct _active_line *link;
  69.     char *name;
  70.     long last;
  71.     long first;
  72.     char type;
  73. } ACTIVE_LINE;
  74.  
  75. #define Nullact Null(ACTIVE_LINE*)
  76.  
  77. ACTIVE_LINE *line_root = Nullact, *last_line = Nullact, *pline = Nullact;
  78.  
  79. bool force_flag = FALSE, kill_mthreads = FALSE, no_processing = FALSE;
  80. bool add_new = FALSE, rebuild = FALSE, zap_thread = FALSE;
  81. bool acttimes_flag = FALSE, grevious_error;
  82. bool initializing = TRUE;
  83. int daemon_delay = 0, log_verbosity = 0, debug = 0, slow_down = 0;
  84. long expire_time = 0;
  85. char *hierarchy_list = NULL;
  86. long truncate_len = -1;
  87.  
  88. char nullstr[1] = "";
  89.  
  90. extern int locked, cron_locking;
  91.  
  92. #ifdef TZSET
  93. time_t tnow;
  94. #else
  95. struct timeb ftnow;
  96. #endif
  97.  
  98. #define TIMER_FIRST 1
  99. #define TIMER_DEFAULT (10 * 60)
  100.  
  101. int processed_groups = 0, added_groups = 0, removed_groups = 0;
  102. int action;
  103.  
  104. #define NG_DEFAULT    0
  105. #define NG_MATCH    1
  106. #define NG_SKIP        2
  107.  
  108. #ifdef SERVER
  109. char *server;
  110. #else
  111. time_t last_modified = 0;
  112. #endif
  113.  
  114. SIGRET alarm_handler(), int_handler();
  115. bool makethreads ANSI((void));
  116. void log_startup ANSI((void));
  117. void log_stats ANSI((void));
  118.  
  119. int
  120. main(argc, argv)
  121. int  argc;
  122. char *argv[];
  123. {
  124.     int fd;
  125.     long pid;
  126.  
  127.     while (--argc) {
  128.     if (**++argv == '-') {
  129.         while (*++*argv) {
  130.         switch (**argv) {
  131.         case 'a':        /* automatically thread new groups */
  132.             add_new = TRUE;
  133.             break;
  134.         case 'c':        /* continue trying to lock */
  135.             cron_locking = TRUE;
  136.             break;
  137.         case 'D':        /* run in debug mode */
  138.             debug++;
  139.             break;
  140.         case 'd':        /* run in daemon mode */
  141.             if (*++*argv <= '9' && **argv >= '0') {
  142.             daemon_delay = atoi(*argv) * 60;
  143.             while (*++*argv <= '9' && **argv >= '0') {
  144.                 ;
  145.             }
  146.             } else {
  147.             daemon_delay = TIMER_DEFAULT;
  148.             }
  149.             --*argv;
  150.             break;
  151.         case 'e': {        /* enhanced expire processing */
  152.             struct tm *ts;
  153.             long desired;
  154.  
  155.             (void) time(&expire_time);
  156.             ts = localtime(&expire_time);
  157.  
  158.             if (*++*argv <= '9' && **argv >= '0') {
  159.             desired = atol(*argv);
  160.             if (desired/100 > 23 || desired%100 > 59) {
  161.                 fprintf(stderr, "Illegal expire time: '%04d'\n",
  162.                 desired);
  163.                 exit(1);
  164.             }
  165.             desired = (desired/100)*60 + desired%100;
  166.             while (*++*argv <= '9' && **argv >= '0') {
  167.                 ;
  168.             }
  169.             } else {
  170.             desired = 30;            /* 0030 = 12:30am */
  171.             }
  172.             --*argv;
  173.             desired -= ts->tm_hour * 60 + ts->tm_min;
  174.             if (desired < 0) {
  175.             desired += 24 * 60;
  176.             }
  177.             expire_time += desired * 60 - ts->tm_sec;
  178.             break;
  179.          }
  180.         case 'f':        /* force each group to process */
  181.             force_flag = TRUE;
  182.             break;
  183.         case 'k':        /* kill running mthreads */
  184.             kill_mthreads = TRUE;
  185.             break;
  186.         case 'n':        /* don't process anything */
  187.             no_processing = TRUE;
  188.             break;
  189.         case 's':        /* sleep between articles */
  190.             if (*++*argv <= '9' && **argv >= '0') {
  191.             slow_down = atoi(*argv);
  192.             while (*++*argv <= '9' && **argv >= '0') {
  193.                 ;
  194.             }
  195.             } else {
  196.             slow_down = 1L * 1000 * 1000;
  197.             }
  198.             --*argv;
  199.             break;
  200.         case 't':        /* maintain active.times file */
  201. #ifdef ACTIVE_TIMES
  202.             if (strEQ(ACTIVE_TIMES, "nntp")) {
  203.             fprintf(stderr, "Ignoring the -t option.\n");
  204.             } else {
  205.             acttimes_flag = TRUE;
  206.             }
  207. #else
  208.             fprintf(stderr, "Ignoring the -t option.\n");
  209. #endif
  210.             break;
  211.         case 'v':        /* get more verbose in the log file */
  212.             log_verbosity++;
  213.             break;
  214.         case 'z':        /* destroy .thread on severe signal */
  215.             zap_thread = TRUE;
  216.             break;
  217.         default:
  218.             fprintf(stderr, "Unknown option: '%c'\n", **argv);
  219.             exit(1);
  220.         }
  221.         }
  222.     } else {
  223.         if (hierarchy_list) {
  224.         fprintf(stderr, "Specify the newsgroups in one comma-separated list.\n");
  225.         exit(1);
  226.         }
  227.         hierarchy_list = *argv;
  228.     }
  229.     }
  230.  
  231.     /* Initialize umask(), file_exp(), etc. */
  232.     mt_init();
  233.  
  234.     /* If this is a kill request, look for the daemon lock file. */
  235.     if (kill_mthreads) {
  236.     if (!mt_lock(DAEMON_LOCK, SIGTERM)) {
  237.         fprintf(stderr, "No mthreads daemon is running.\n");
  238.         wrap_it_up(1);
  239.     }
  240.     fprintf(stderr, "Killed mthreads daemon.\n");
  241.     wrap_it_up(0);
  242.     }
  243.  
  244.     /* See if an mthreads pass is already going. */
  245.     if (mt_lock(PASS_LOCK, 0) != 0 && !daemon_delay) {
  246.     fprintf(stderr, "mthreads is already running.\n");
  247.     wrap_it_up(1);
  248.     }
  249.  
  250. #ifdef USESYSLOG
  251. # ifdef LOG_DAEMON
  252.     openlog("mthreads", LOG_PID, USESYSLOG);
  253. # else
  254.     openlog("mthreads", LOG_PID);
  255. # endif
  256. #else
  257.     /* Open our log file */
  258.     filename = file_exp(MTLOG);
  259.     if ((fp_log = fopen(filename, "a")) == Nullfp) {
  260.     fprintf(stderr, "Unable to open `%s'.\n", filename);
  261.     wrap_it_up(1);
  262.     }
  263. #endif
  264.  
  265. #ifdef SIGHUP
  266.     if (sigset(SIGHUP, SIG_IGN) != SIG_IGN) {
  267.     sigset(SIGHUP, int_handler);
  268.     }
  269. #endif
  270.     if (sigset(SIGINT, SIG_IGN) != SIG_IGN) {
  271.     sigset(SIGINT, int_handler);
  272.     }
  273. #ifdef SIGQUIT
  274.     if (sigset(SIGQUIT, SIG_IGN) != SIG_IGN) {
  275.     sigset(SIGQUIT, int_handler);
  276.     }
  277. #endif
  278.     sigset(SIGTERM, int_handler);
  279. #ifdef SIGBUS
  280.     sigset(SIGBUS, int_handler);
  281. #endif
  282.     sigset(SIGSEGV, int_handler);
  283. #ifdef SIGTTIN
  284.     sigset(SIGTTIN, SIG_IGN);
  285.     sigset(SIGTTOU, SIG_IGN);
  286. #endif
  287.     sigset(SIGALRM, SIG_IGN);
  288. #ifdef SERVER
  289.     sigset(SIGPIPE, int_handler);
  290. #endif
  291. #ifdef lint
  292.     alarm_handler();            /* foolishness for lint's sake */
  293.     int_handler(SIGINT);
  294. #endif
  295.  
  296.     /* Ensure this machine has the right byte-order for the database */
  297.     filename = file_exp(DBINIT);
  298.     if ((fp_tmp = fopen(filename, FOPEN_RB)) == Nullfp
  299.      || fread((char*)&mt_bmap,1,sizeof (BMAP), fp_tmp) < sizeof (BMAP)-1) {
  300.     if (fp_tmp != Nullfp) {
  301.         fclose(fp_tmp);
  302.     }
  303.      write_db_init:
  304.     mybytemap(&mt_bmap);
  305.     if ((fp_tmp = fopen(filename, FOPEN_WB)) == Nullfp) {
  306.         log_entry("Unable to create file: `%s'.\n", filename);
  307.         wrap_it_up(1);
  308.     }
  309.     mt_bmap.version = DB_VERSION;
  310.     fwrite((char*)&mt_bmap, 1, sizeof (BMAP), fp_tmp);
  311.     fclose(fp_tmp);
  312.     } else {
  313.     int i;
  314.  
  315.     fclose(fp_tmp);
  316.     if (mt_bmap.version != DB_VERSION) {
  317.         if (mt_bmap.version == DB_VERSION-1) {
  318.         rebuild = TRUE;
  319.         log_entry("Upgrading database to version %d.\n", DB_VERSION);
  320.         goto write_db_init;
  321.         }
  322.         log_entry("** Database is not the right version (%d instead of %d) **\n",
  323.         mt_bmap.version, DB_VERSION);
  324.         wrap_it_up(1);
  325.     }
  326.     mybytemap(&my_bmap);
  327.     for (i = 0; i < sizeof (LONG); i++) {
  328.         if (my_bmap.l[i] != mt_bmap.l[i]
  329.          || (i < sizeof (WORD) && my_bmap.w[i] != mt_bmap.w[i])) {
  330.         log_entry("\
  331. ** Byte-order conflict -- re-run from a compatible machine **\n\
  332. \t\tor remove the current thread files, including db.init **\n");
  333.         wrap_it_up(1);
  334.         }
  335.     }
  336.     }
  337.  
  338. #ifdef SERVER
  339.     if ((server = get_server_name(0)) == NULL) {
  340.     log_entry("Couldn't find name of news server.\n");
  341.     wrap_it_up(1);
  342.     }
  343. #endif
  344.  
  345.     initializing = FALSE;
  346.  
  347.     /* If we're not in daemon mode, run through once and quit. */
  348.     if (!daemon_delay) {
  349.     log_startup();
  350.     setbuf(stdout, Nullch);
  351.     extra_expire = (expire_time != 0);
  352.     makethreads();
  353.     } else {
  354.     cron_locking = FALSE;
  355.     if (mt_lock(DAEMON_LOCK, 0) != 0) {
  356.         fprintf(stderr, "An mthreads daemon is already running.\n");
  357.         wrap_it_up(1);
  358.     }
  359.     /* For daemon mode, we cut ourself off from anything tty-related and
  360.     ** run in the background (involves forks, but no knives).
  361.     */
  362.     close(0);
  363.     if (open("/dev/null", 2) != 0) {
  364.         fprintf(stderr, "unable to open /dev/null!\n");
  365.         wrap_it_up(1);
  366.     }
  367.     close(1);
  368.     close(2);
  369.     dup(0);
  370.     dup(0);
  371.     while ((pid = fork()) < 0) {
  372.         sleep(2);
  373.     }
  374.     if (pid) {
  375.         exit(0);
  376.     }
  377. #ifdef TIOCNOTTY
  378.     if ((fd = open("/dev/tty", 1)) >= 0) {
  379.         ioctl(fd, TIOCNOTTY, (int*)0);
  380.         close(fd);
  381.     }
  382. #else
  383.     (void) setpgrp();
  384.     while ((pid = fork()) < 0) {
  385.         sleep(2);
  386.     }
  387.     if (pid) {
  388.         exit(0);
  389.     }
  390. #endif
  391.     /* Put our pid in the lock file for death detection */
  392.     if ((fp_tmp = fopen(file_exp(MTDLOCK), "w")) != Nullfp) {
  393.         fprintf(fp_tmp, "%ld\n", (long)getpid());
  394.         fclose(fp_tmp);
  395.     }
  396.     log_startup();
  397.  
  398.     sigset(SIGALRM, alarm_handler);
  399.  
  400.     /* Start timer -- first interval is shorter than all others */
  401.     alarm(TIMER_FIRST);
  402.     for (;;) {
  403.         pause();        /* let alarm go off */
  404.         alarm(0);
  405.  
  406. #ifndef USESYSLOG
  407.         /* Re-open our log file, if needed */
  408.         if (!fp_log && !(fp_log = fopen(file_exp(MTLOG), "a"))) {
  409.         wrap_it_up(1);
  410.         }
  411. #endif
  412. #ifndef SERVER
  413.         if (stat(file_exp(ACTIVE), &filestat) < 0) {
  414.         log_entry("Unable to stat active file -- quitting.\n");
  415.         wrap_it_up(1);
  416.         }
  417. #endif
  418.         if (expire_time && time(Null(time_t*)) > expire_time) {
  419.         expire_time += 24L * 60 * 60;
  420.         extra_expire = TRUE;
  421.         }
  422. #ifdef SERVER
  423.         makethreads();    /* NNTP version always compares files */
  424. #else
  425.         if (extra_expire || filestat.st_mtime != last_modified) {
  426.         last_modified = filestat.st_mtime;
  427.         if (!makethreads()) {
  428.             last_modified--;
  429.         }
  430.         }
  431. #endif
  432.         alarm(daemon_delay);
  433. #ifndef USESYSLOG
  434.         fclose(fp_log);    /* close the log file while we sleep */
  435.         fp_log = Nullfp;
  436. #endif
  437.     } /* for */
  438.     }/* if */
  439.  
  440.     wrap_it_up(0);
  441.     return 0;                /* NOTREACHED */
  442. }
  443.  
  444. SIGRET
  445. alarm_handler()
  446. {
  447.     sigset(SIGALRM, alarm_handler);
  448. }
  449.  
  450. SIGRET
  451. int_handler(sig)
  452. int sig;
  453. {
  454.     static int visits = 0;
  455.     int ret = 0;
  456.  
  457.     if (++visits > 4) {
  458.     wrap_it_up(1);
  459.     }
  460.  
  461. #ifndef USESYSLOG
  462.     /* Re-open our log file, if needed */
  463.     if (fp_log || (fp_log = fopen(file_exp(MTLOG), "a")))
  464. #endif
  465.     {
  466.     switch (sig) {
  467.     case SIGTERM:
  468. #ifdef SIGHUP
  469.     case SIGHUP:
  470. #endif
  471. #ifdef SIGQUIT
  472.     case SIGQUIT:
  473. #endif
  474.         log_entry("halt requested.\n");
  475.         zap_thread = 0;
  476.         break;
  477. #ifdef SERVER
  478.     case SIGPIPE:
  479.         log_entry("broken pipe -- trying to continue.\n");
  480.         sigset(SIGPIPE, int_handler);
  481.         return;
  482. #endif
  483. #ifdef SIGBUS
  484.         case SIGBUS:
  485. #endif
  486.         case SIGSEGV:
  487.         log_error("** Severe signal: %d **\n", sig);
  488.         /* Destroy offending thread file if requested to do so. */
  489.         if (zap_thread) {
  490.             unlink(thread_name(line));
  491.             log_entry("Destroyed thread file for %s\n", line);
  492.         }
  493.         break;
  494.     default:
  495.         log_entry("interrupt %d received.\n", sig);
  496.         zap_thread = 0;
  497.         ret = 1;
  498.         break;
  499.     }
  500.     }
  501.     if (!daemon_delay) {
  502.     printf("Interrupt %d!\n", sig);
  503.     if (zap_thread) {
  504.         printf("Destroyed thread file for %s\n", line);
  505.     }
  506.     }
  507.  
  508.     /* If we're in the middle of writing the new active2 file, finish it. */
  509.     if (fp_active2w) {
  510.     if (*line2) {
  511.         if (index(line2, ' ')) {
  512.         fputs(line2, fp_active2w);
  513.         } else {
  514.         fprintf(fp_active2w, fmt_active2, line2, last2, first2, ch2);
  515.         }
  516.     }
  517.     for (pline = line_root; pline; pline = pline->link) {
  518.         fprintf(fp_active2w, fmt_active2,
  519.             pline->name, pline->last, pline->first, pline->type);
  520.     }
  521.     if (!eof_active2) {
  522.         while (fgets(line2, sizeof line2, fp_active2)) {
  523.         fputs(line2, fp_active2w);
  524.         }
  525.     }
  526.     log_stats();
  527.     }
  528.     wrap_it_up(ret);
  529. }
  530.  
  531. void
  532. wrap_it_up(ret)
  533. int ret;
  534. {
  535.     mt_unlock(locked);
  536.     exit(ret);
  537. }
  538.  
  539. /* Process the active file, creating/modifying the active2 file and
  540. ** creating/modifying the thread data files.
  541. */
  542. bool
  543. makethreads()
  544. {
  545.     register char *cp, *cp2;
  546.     char data_file_open;
  547.     bool update_successful, old_groups, touch_thread;
  548. #ifdef SERVER
  549.     int server_failure = 0;
  550. #endif
  551.  
  552.     /* See if an mthreads pass is already going. */
  553.     if (!(locked & PASS_LOCK) && mt_lock(PASS_LOCK, 0) != 0) {
  554.     log_entry("unable to get a lock for this pass.\n");
  555.     return FALSE;
  556.     }
  557. #ifdef SERVER
  558.     if (!open_server()) {
  559.     return FALSE;
  560.     }
  561.     put_server("LIST");    /* ask server for the active file */
  562.     get_server(line, sizeof line);
  563.     if (*line != CHAR_OK) {
  564.     log_entry("Unable to get active file from server.\n");
  565.     close_server();
  566.     return FALSE;
  567.     }
  568.     if ((fp_active = fopen(file_exp(ACTIVE1), "w+")) == Nullfp) {
  569.     log_entry("Unable to write the active1 file -- quitting.\n");
  570.     wrap_it_up(1);
  571.     }
  572.     while (1) {
  573.     if (get_server(line, sizeof line) < 0) {
  574.         log_entry("Server failed to send entire active file.\n");
  575.         fclose(fp_active);
  576.         close_server();
  577.         return FALSE;
  578.     }
  579.     if (*line == '.') {
  580.         break;
  581.     }
  582.     fputs(line, fp_active);
  583.     putc('\n', fp_active);
  584.     }
  585.     if (ferror(fp_active)) {
  586.     log_entry("Error writing to active1 file.\n");
  587.     fclose(fp_active);
  588.     close_server();
  589.     return FALSE;
  590.     }
  591.     fseek(fp_active, 0L, 0);        /* rewind for read */
  592. #else /* not SERVER */
  593.     if ((fp_active = fopen(file_exp(ACTIVE), "r")) == Nullfp) {
  594.     log_entry("Unable to open the active file.\n");
  595.     wrap_it_up(1);
  596.     }
  597. #endif
  598.     filename = file_exp(ACTIVE2);
  599.     if ((fp_active2w = fopen(filename, "r+")) == Nullfp) {
  600.     if ((fp_active2w = fopen(filename, "w")) == Nullfp) {
  601.         log_entry("Unable to open the active2 file for update.\n");
  602.         wrap_it_up(1);
  603.     }
  604.     /* Add existing groups to active.times file with ancient date. */
  605.     old_groups = TRUE;
  606.     } else {
  607.     old_groups = FALSE;
  608.     }
  609.     if ((fp_active2 = fopen(filename, "r")) == Nullfp) {
  610.     log_entry("Unable to open the active2 file.\n");
  611.     wrap_it_up(1);
  612.     }
  613.     if (extra_expire && log_verbosity) {
  614.     log_entry("Using enhanced expiration for this pass.\n");
  615.     }
  616.  
  617.     /* What time is it? */
  618. #ifdef TZSET
  619.     (void) time(&tnow);
  620.     (void) tzset();
  621. #else
  622.     (void) ftime(&ftnow);
  623. #endif
  624.  
  625.     eof_active = eof_active2 = FALSE;
  626.     fp_tmp = Nullfp;
  627.  
  628.     /* Loop through entire active file. */
  629.     for (;;) {
  630.     if (eof_active || !fgets(line, sizeof line, fp_active)) {
  631.         if (eof_active2 && !line_root) {
  632.         break;
  633.         }
  634.         eof_active = TRUE;
  635.         ch = 'x';
  636.     } else {
  637.         cp = line + strlen(line) - 1;
  638.         if (*cp == '\n') {
  639.         *cp = '\0';
  640.         }
  641.         if (!(cp = index(line, ' '))) {
  642.         log_entry("** line in 'active' has no space: %s **\n", line);
  643.         continue;
  644.         }
  645.         *cp = '\0';
  646.         if (sscanf(cp+1, "%ld %ld %c", &last, &first, &ch) != 3) {
  647.         log_entry("** digits corrupted in 'active': %s %s **\n",
  648.             line, cp+1);
  649.         continue;
  650.         }
  651.         if (last < first - 1) {
  652.         log_entry("** bogus group values in 'active': %s %s **\n",
  653.             line, cp+1);
  654.         continue;
  655.         }
  656.     }
  657.     if (debug > 1 || log_verbosity > 3) {
  658.         log_entry("Processing %s:\n", line);
  659.     }
  660.     data_file_open = 0;
  661.     /* If we've allocated some lines in memory while searching for
  662.     ** newsgroups (they've scrambled the active file on us), check
  663.     ** them first.
  664.     */
  665.     last_line = Nullact;
  666.     for (pline = line_root; pline; pline = pline->link) {
  667.         if (eof_active || strEQ(line, pline->name)) {
  668.         strcpy(line2, pline->name);
  669.         free(pline->name);
  670.         first2 = pline->first;
  671.         last2 = pline->last;
  672.         ch2 = pline->type;
  673.         if (last_line) {
  674.             last_line->link = pline->link;
  675.         } else {
  676.             line_root = pline->link;
  677.         }
  678.         free(pline);
  679.         break;
  680.         }
  681.         last_line = pline;
  682.     }/* for */
  683.     touch_thread = FALSE;
  684.  
  685.     /* If not found yet, check the active2 file. */
  686.     if (!pline) {
  687.         for (;;) {
  688.         if (eof_active2 || !fgets(line2, sizeof line2, fp_active2)) {
  689.             /* At end of file, check if the thread data file exists.
  690.             ** If so, use its high/low values.  Else, default to
  691.             ** some initial values.
  692.             */
  693.             eof_active2 = TRUE;
  694.             if (eof_active) {
  695.             break;
  696.             }
  697.             strcpy(line2, line);
  698.             if ((data_file_open = init_data(thread_name(line)))) {
  699.             last2 = total.last;
  700.             first2 = total.first;
  701.             ch2 = 'y';
  702.             } else {
  703.             total.first = first2 = first;
  704.             if (add_new && (!hierarchy_list
  705.               || ngmatch(hierarchy_list, line) == NG_MATCH)) {
  706.                 total.last = last2 = first - 1;
  707.                 ch2 = (ch == '=' ? 'x' : ch);
  708.                 touch_thread = TRUE;
  709.                 added_groups += (ch2 != 'x');
  710.             } else {
  711.                 total.last = last2 = last;
  712.                 ch2 = (ch == '=' ? 'X' : toupper(ch));
  713.             }
  714.             }
  715.             data_file_open++;        /* (1 == empty, 2 == open) */
  716. #ifdef ACTIVE_TIMES
  717.             /* Found a new group -- see if we need to log it. */
  718.             if (acttimes_flag) {
  719.             if (!fp_tmp && !(fp_tmp = fopen(ACTIVE_TIMES, "a"))) {
  720.                 log_entry("unable to append to %s.\n",ACTIVE_TIMES);
  721.                 acttimes_flag = FALSE;
  722.             } else {
  723.                 fprintf(fp_tmp, "%s %ld mthreads@%s\n", line,
  724.                 old_groups ? 30010440L : time(Null(time_t)),
  725.                 OURDOMAIN);
  726.             }
  727.             }
  728. #endif
  729.             break;
  730.         }
  731.         if (!(cp2 = index(line2, ' '))) {
  732.             log_entry("active2 line has no space: %s\n", line2);
  733.             continue;
  734.         }
  735.         *cp2 = '\0';
  736.         if (sscanf(cp2+1,"%ld %ld %c",&last2,&first2,&ch2) != 3) {
  737.             log_entry("active2 digits corrupted: %s %s\n",
  738.             line2, cp2+1);
  739.             continue;
  740.         }
  741.         /* Check if we're still in-sync */
  742.         if (eof_active || strEQ(line, line2)) {
  743.             break;
  744.         }
  745.         /* Nope, we've got to go looking for this line somewhere
  746.         ** down in the file.  Save each non-matching line in memory
  747.         ** as we go.
  748.         */
  749.         pline = (ACTIVE_LINE*)safemalloc(sizeof (ACTIVE_LINE));
  750.         pline->name = savestr(line2);
  751.         pline->last = last2;
  752.         pline->first = first2;
  753.         pline->type = ch2;
  754.         pline->link = Nullact;
  755.         if (!last_line) {
  756.             line_root = pline;
  757.         } else {
  758.             last_line->link = pline;
  759.         }
  760.         *line2 = '\0';
  761.         last_line = pline;
  762.         }/* for */
  763.         if (eof_active && eof_active2) {
  764.         break;
  765.         }
  766.     }/* if !pline */
  767.     if (eof_active) {
  768.         strcpy(line, line2);
  769.         if (truncate_len < 0) {
  770.         truncate_len = ftell(fp_active2w);
  771.         }
  772.     }
  773.     if (rebuild) {
  774.         unlink(thread_name(line));
  775.     }
  776.     update_successful = FALSE;
  777.     if (hierarchy_list && !add_new) {
  778.         switch ((action = ngmatch(hierarchy_list, line))) {
  779.         case NG_MATCH:            /* if unthreaded, add it */
  780.         if (ch2 < 'a' && ch != 'x' && ch != '=') {
  781.             total.last = last2 = first2 - 1;
  782.             touch_thread = TRUE;
  783.             added_groups++;
  784.         }
  785.         break;
  786.         case NG_SKIP:            /* if threaded, remove it */
  787.         if (ch2 >= 'a') {
  788.             unlink(thread_name(line));
  789.             removed_groups++;
  790.         }
  791.         break;
  792.         }
  793.     } else {
  794.         action = (ch2 < 'a' ? NG_SKIP : NG_MATCH);
  795.     }
  796.     if (action == NG_DEFAULT || (debug && action == NG_SKIP)) {
  797.         dont_read_data(data_file_open);    /* skip silently */
  798.         if (touch_thread) {
  799.         (void) write_data(thread_name(line));
  800.         }
  801.     } else if (ch == 'x' || ch == '=') {
  802.         if (!daemon_delay) {        /* skip 'x'ed groups */
  803.         putchar('x');
  804.         }
  805.         ch = (action == NG_SKIP ? 'X' : 'x');
  806.         if ((ch2 >= 'a' && ch2 != 'x') || force_flag) {
  807.         /* Remove thread file if group is newly 'x'ed out */
  808.         unlink(thread_name(line));
  809.         }
  810.         update_successful = TRUE;
  811.         dont_read_data(data_file_open);
  812.     } else if (action == NG_SKIP) {    /* skip excluded groups */
  813.         if (!daemon_delay) {
  814.         putchar('X');
  815.         }
  816.         ch = toupper(ch);
  817.         if (force_flag) {
  818.         unlink(thread_name(line));
  819.         }
  820.         update_successful = TRUE;
  821.         dont_read_data(data_file_open);
  822.     } else if (no_processing) {
  823.         if (!daemon_delay) {
  824.         putchar(',');
  825.         }
  826.         ch2 = ch;
  827.         dont_read_data(data_file_open);
  828.         if (touch_thread) {
  829.         (void) write_data(thread_name(line));
  830.         }
  831.     } else if (!force_flag && !extra_expire && !rebuild
  832.      && first == first2 && last == last2) {
  833.         /* We're up-to-date here.  Skip it. */
  834.         if (!daemon_delay) {
  835.         putchar('.');
  836.         }
  837.         update_successful = TRUE;
  838.         dont_read_data(data_file_open);
  839.         if (touch_thread) {
  840.         (void) write_data(thread_name(line));
  841.         }
  842.     } else {
  843.         /* Looks like we need to process something. */
  844. #ifdef SERVER
  845.         if (server_failure == 1) {
  846.         if (!open_server()) {
  847.             log_entry("failed to re-open server for this pass.\n");
  848.             server_failure = 2;
  849.         } else {
  850.             server_failure = 0;
  851.         }
  852.         }
  853.         if (!server_failure) {
  854.         sprintf(buf, "GROUP %s", line);
  855.         put_server(buf);        /* go to next group */
  856.         if (get_server(buf, sizeof buf) < 0
  857.          || *buf != CHAR_OK) {
  858.             log_error("NNTP failure -- %s.\n", buf);
  859.             if (strnNE(buf, "400", 3)) {
  860.             close_server();
  861.             }
  862.             server_failure = 1;
  863.         }
  864.         }
  865.         if (server_failure) {
  866. #else
  867.         strcpy(cp = buf, line2);
  868.         while ((cp = index(cp, '.'))) {
  869.         *cp = '/';
  870.         }
  871.         filename = file_exp(buf);        /* relative to spool dir */
  872.         if (chdir(filename) < 0) {
  873.         if (errno != ENOENT) {
  874.             log_entry("Unable to chdir to `%s'.\n", filename);
  875.         }
  876. #endif
  877.         if (!daemon_delay) {
  878.             putchar('*');
  879.         }
  880.         dont_read_data(data_file_open);
  881.         } else {
  882.         filename = thread_name(line);
  883.         /* Try to open the data file only if we didn't try it
  884.         ** in the name matching code above.
  885.         */
  886.         if (!data_file_open--) {    /* (0 == haven't tried yet) */
  887.             if (!(data_file_open = init_data(filename))) {
  888.             total.last = first - 1;
  889.             total.first = first;
  890.             }
  891.         }
  892.  
  893.         if (data_file_open) {        /* (0 == empty, 1 == open) */
  894.             if (!read_data()) {    /* did read fail? */
  895.             if (debug) {
  896.                 strcpy(buf, filename);
  897.                 cp = rindex(buf, '/') + 1;
  898.                 strcpy(cp, "bad.read");
  899.                 rename(filename, buf);
  900.             }
  901.             data_file_open = init_data(Nullch);
  902.             total.last = first - 1;
  903.             total.first = first;
  904.             }
  905.         }
  906.         grevious_error = FALSE;
  907.         process_articles(first, last);
  908.         processed_groups++;
  909.         if (!added_count && !expired_count && !touch_thread
  910.          && last == last2) {
  911.             (void) write_data(Nullch);
  912.             if (!daemon_delay) {
  913.             putchar(':');
  914.             }
  915.             update_successful = TRUE;
  916.         } else {
  917.             strcpy(buf, filename);
  918.             cp = rindex(buf, '/') + 1;
  919.             strcpy(cp, NEW_THREAD);    /* write data as .new */
  920.             if (write_data(buf) && !grevious_error) {
  921.             rename(buf, filename);
  922.             added_articles += added_count;
  923.             expired_articles += expired_count;
  924.             if (!daemon_delay) {
  925.                 if (!total.root) {
  926.                 putchar('-');
  927.                 } else {
  928.                 putchar('#');
  929.                 }
  930.             }
  931.             update_successful = TRUE;
  932.             } else {
  933.             if (debug) {
  934.                 cp = rindex(filename, '/') + 1;
  935.                 strcpy(cp, "bad.write");
  936.                 rename(buf, filename);
  937.             } else {
  938.                 unlink(buf);    /* blow away bad write */
  939.                 if (grevious_error) {
  940.                 unlink(filename); /* blow away the .thread, */
  941.                 (void) init_data(Nullch); /* zero totals & */
  942.                 (void) write_data(filename); /* write it null */
  943.                 }
  944.             }
  945.             if (!daemon_delay) {
  946.                 putchar('!');
  947.             }
  948.             }/* if */
  949.         }/* if */
  950.         }/* if */
  951.     }/* if */
  952.     /* Finally, update the active2 entry for this newsgroup. */
  953.     if (!eof_active) {
  954.         if (update_successful) {
  955.         fprintf(fp_active2w, fmt_active2, line, last, first, ch);
  956.         } else {
  957.         fprintf(fp_active2w, fmt_active2, line, last2, first2, ch2);
  958.         }
  959.     }
  960.     *line2 = '\0';
  961.     /* If we're not out of sync, keep active2 file flushed. */
  962.     if (!line_root) {
  963.         fflush(fp_active2w);
  964.     }
  965. #ifdef CHECKLOAD
  966.     checkload();
  967. #endif
  968.     }/* for */
  969.  
  970. #ifdef SERVER
  971.     if (!server_failure) {
  972.     close_server();
  973.     }
  974. #endif
  975.     fclose(fp_active);
  976.     fclose(fp_active2);
  977.  
  978.     if (truncate_len >= 0) {
  979. #ifdef FTRUNCATE
  980.     if (ftruncate(fileno(fp_active2w), truncate_len) == -1)
  981. #else
  982. #ifdef MVTRUNC
  983.     if (mvtrunc(file_exp(ACTIVE2), truncate_len) == -1)
  984. #else
  985.     if (chsize(fileno(fp_active2w), truncate_len) == -1)
  986. #endif
  987. #endif
  988.     {
  989.         log_entry("Unable to truncate the active2 file.\n");
  990.     }
  991.     truncate_len = -1;
  992.     }
  993.     fclose(fp_active2w);
  994.     fp_active2w = Nullfp;
  995.  
  996.     if (fp_tmp) {
  997.     fclose(fp_tmp);
  998.     }
  999.     log_stats();
  1000.     processed_groups = added_groups = removed_groups = 0;
  1001.     added_articles = expired_articles = 0;
  1002.  
  1003.     extra_expire = FALSE;
  1004.     rebuild = FALSE;
  1005.  
  1006.     mt_unlock(PASS_LOCK);        /* remove single-pass lock */
  1007.  
  1008.     return TRUE;
  1009. }
  1010.  
  1011. #ifdef SERVER
  1012. int
  1013. open_server()
  1014. {
  1015.     switch (server_init(server)) {
  1016.     case OK_NOPOST:
  1017.     case OK_CANPOST:
  1018.     return 1;
  1019.     case ERR_ACCESS:
  1020.     log_entry("Server %s rejected connection -- quitting.\n", server);
  1021.     wrap_it_up(1);
  1022.     default:
  1023.     log_entry("Couldn't connect with server %s.\n", server);
  1024.     return 0;
  1025.     }
  1026. }
  1027. #endif
  1028.  
  1029. /*
  1030. ** ngmatch - newsgroup name matching
  1031. **
  1032. ** returns NG_MATCH for a positive patch, NG_SKIP for a negative match,
  1033. ** and NG_DEFAULT if the group doesn't match at all.
  1034. **
  1035. ** "all" in a pattern is a wildcard that matches exactly one word;
  1036. ** it does not cross "." (NGDELIM) delimiters.
  1037. **
  1038. ** This matching code was borrowed from C news.
  1039. */
  1040.  
  1041. #define ALL "all"            /* word wildcard */
  1042.  
  1043. #define NGNEG '!'
  1044. #define NGSEP ','
  1045. #define NGDELIM '.'
  1046.  
  1047. int
  1048. ngmatch(ngpat, grp)
  1049. char *ngpat, *grp;
  1050. {
  1051.     register char *patp;        /* point at current pattern */
  1052.     register char *patcomma;
  1053.     register int depth;
  1054.     register int faildeepest = 0, hitdeepest = 0;    /* in case no match */
  1055.     register bool negation;
  1056.  
  1057.     for (patp = ngpat; patp != Nullch; patp = patcomma) {
  1058.     negation = FALSE;
  1059.     patcomma = index(patp, NGSEP);
  1060.     if (patcomma != Nullch) {
  1061.         *patcomma = '\0';        /* will be restored below */
  1062.     }
  1063.     if (*patp == NGNEG) {
  1064.         ++patp;
  1065.         negation = TRUE;
  1066.     }
  1067.     depth = onepatmatch(patp, grp); /* try 1 pattern, 1 group */
  1068.     if (patcomma != Nullch) {
  1069.         *patcomma++ = NGSEP;    /* point after the comma */
  1070.     }
  1071.     if (depth == 0) {        /* mis-match */
  1072.         ;                /* ignore it */
  1073.     } else if (negation) {
  1074.         /* record depth of deepest negated matched word */
  1075.         if (depth > faildeepest) {
  1076.         faildeepest = depth;
  1077.         }
  1078.     } else {
  1079.         /* record depth of deepest plain matched word */
  1080.         if (depth > hitdeepest) {
  1081.         hitdeepest = depth;
  1082.         }
  1083.     }
  1084.     }
  1085.     if (hitdeepest > faildeepest) {
  1086.     return NG_MATCH;
  1087.     } else if (faildeepest) {
  1088.     return NG_SKIP;
  1089.     } else {
  1090.     return NG_DEFAULT;
  1091.     }
  1092. }
  1093.  
  1094. /*
  1095. ** Match a pattern against a group by looking at each word of pattern in turn.
  1096. **
  1097. ** On a match, return the depth (roughly, ordinal number * k) of the rightmost
  1098. ** word that matches.  If group runs out first, the match fails; if pattern
  1099. ** runs out first, it succeeds.  On a failure, return zero.
  1100. */
  1101. int
  1102. onepatmatch(patp, grp)
  1103. char *patp, *grp;
  1104. {
  1105.     register char *rpatwd;        /* used by word match (inner loop) */
  1106.     register char *patdot, *grdot;    /* point at dots after words */
  1107.     register char *patwd, *grwd;    /* point at current words */
  1108.     register int depth = 0;
  1109.  
  1110.     for (patwd = patp, grwd = grp;
  1111.      patwd != Nullch && grwd != Nullch;
  1112.      patwd = patdot, grwd = grdot
  1113.     ) {
  1114.     register bool match = FALSE;
  1115.     register int incr = 20;
  1116.  
  1117.     /* null-terminate words */
  1118.     patdot = index(patwd, NGDELIM);
  1119.     if (patdot != Nullch) {
  1120.         *patdot = '\0';        /* will be restored below */
  1121.     }
  1122.     grdot = index(grwd, NGDELIM);
  1123.     if (grdot != Nullch) {
  1124.         *grdot = '\0';        /* will be restored below */
  1125.     }
  1126.     /*
  1127.      * Match one word of pattern with one word of group.
  1128.      * A pattern word of "all" matches any group word,
  1129.      * but isn't worth as much.
  1130.      */
  1131. #ifdef FAST_STRCMP
  1132.     match = STREQ(patwd, grwd);
  1133.     if (!match && STREQ(patwd, ALL)) {
  1134.         match = TRUE;
  1135.         --incr;
  1136.     }
  1137. #else
  1138.     for (rpatwd = patwd; *rpatwd == *grwd++;) {
  1139.         if (*rpatwd++ == '\0') {
  1140.         match = TRUE;        /* literal match */
  1141.         break;
  1142.         }
  1143.     }
  1144.     if (!match) {
  1145.         /* ugly special case match for "all" */
  1146.         rpatwd = patwd;
  1147.         if (*rpatwd++ == 'a' && *rpatwd++ == 'l'
  1148.          && *rpatwd++ == 'l' && *rpatwd   == '\0') {
  1149.         match = TRUE;
  1150.          --incr;
  1151.         }
  1152.     }
  1153. #endif                /* FAST_STRCMP */
  1154.  
  1155.     if (patdot != Nullch) {
  1156.         *patdot++ = NGDELIM;    /* point after the dot */
  1157.     }
  1158.     if (grdot != Nullch) {
  1159.         *grdot++ = NGDELIM;
  1160.     }
  1161.     if (!match) {
  1162.         depth = 0;        /* words differed - mismatch */
  1163.         break;
  1164.     }
  1165.     depth += incr;
  1166.     }
  1167.     /* if group name ran out before pattern, then match fails */
  1168.     if (grwd == Nullch && patwd != Nullch) {
  1169.     depth = 0;
  1170.     }
  1171.     return depth;
  1172. }
  1173.  
  1174. /* Put our startup options into the log file.
  1175. */
  1176. void
  1177. log_startup()
  1178. {
  1179.     char tmpbuf[256];
  1180.  
  1181.     strcpy(tmpbuf, "Started mthreads");
  1182.     if (cron_locking) {
  1183.     strcat(tmpbuf, " -c");
  1184.     }
  1185.     if (debug) {
  1186.     strcat(tmpbuf, " -D");
  1187.     }
  1188.     if (force_flag) {
  1189.     strcat(tmpbuf, " -f");
  1190.     }
  1191.     if (daemon_delay) {
  1192.     sprintf(tmpbuf + strlen(tmpbuf), " -d%d", daemon_delay / 60);
  1193.     if (expire_time) {
  1194.         struct tm *ts;
  1195.  
  1196.         ts = localtime(&expire_time);
  1197.         sprintf(tmpbuf + strlen(tmpbuf), " -e%02d%02d",ts->tm_hour,ts->tm_min);
  1198.     }
  1199.     } else if (expire_time) {
  1200.     strcat(tmpbuf, " -e");
  1201.     }
  1202.     if (slow_down) {
  1203.     sprintf(tmpbuf + strlen(tmpbuf), " -s%d", slow_down);
  1204.     }
  1205.     if (no_processing) {
  1206.     strcat(tmpbuf, " -n");
  1207.     }
  1208.     if (log_verbosity) {
  1209.     sprintf(tmpbuf + strlen(tmpbuf), " -v%d", log_verbosity);
  1210.     }
  1211.     if (zap_thread) {
  1212.     strcat(tmpbuf, " -z");
  1213.     }
  1214.     if (add_new) {
  1215.     if (hierarchy_list) {
  1216.         sprintf(tmpbuf + strlen(tmpbuf), " -a %s", hierarchy_list);
  1217.     } else {
  1218.         strcat(tmpbuf, " -a all");
  1219.     }
  1220.     } else if (hierarchy_list) {
  1221.     sprintf(tmpbuf + strlen(tmpbuf), " %s (only)", hierarchy_list);
  1222.     }
  1223.     log_entry("%s\n", tmpbuf);
  1224. }
  1225.  
  1226. /* Put our statistics into the log file.
  1227. */
  1228. void
  1229. log_stats()
  1230. {
  1231.     sprintf(line, "Processed %d group%s:  added %d article%s, expired %d.\n",
  1232.     processed_groups, processed_groups == 1 ? nullstr : "s",
  1233.     added_articles, added_articles == 1 ? nullstr : "s",
  1234.     expired_articles);
  1235.  
  1236.     log_entry(line);
  1237.  
  1238.     if (!daemon_delay) {
  1239.     putchar('\n');
  1240.     fputs(line, stdout);
  1241.     }
  1242.     if (added_groups) {
  1243.     sprintf(line, "Turned %d group%s on.\n", added_groups,
  1244.         added_groups == 1 ? nullstr : "s");
  1245.     log_entry(line);
  1246.     if (!daemon_delay) {
  1247.         fputs(line, stdout);
  1248.     }
  1249.     }
  1250.     if (removed_groups) {
  1251.     sprintf(line, "Turned %d group%s off.\n", removed_groups,
  1252.         removed_groups == 1 ? nullstr : "s");
  1253.     log_entry(line);
  1254.     if (!daemon_delay) {
  1255.         fputs(line, stdout);
  1256.     }
  1257.     }
  1258. }
  1259. /* Generate a log entry with timestamp.
  1260. */
  1261. /*VARARGS1*/
  1262. void
  1263. log_entry(fmt, arg1, arg2, arg3)
  1264. char *fmt;
  1265. long arg1, arg2, arg3;
  1266. {
  1267. #ifndef USESYSLOG
  1268.     time_t now;
  1269.     char *ctime();
  1270. #endif
  1271.  
  1272.     if (initializing) {
  1273.     fprintf(stderr, fmt, arg1, arg2, arg3);
  1274.     return;
  1275.     }
  1276.  
  1277. #ifndef USESYSLOG
  1278.     (void) time(&now);
  1279.     fprintf(fp_log, "%.12s%c", ctime(&now)+4, daemon_delay ? ' ' : '+');
  1280.     fprintf(fp_log, fmt, arg1, arg2, arg3);
  1281.     fflush(fp_log);
  1282. #else
  1283.     syslog(LOG_INFO, fmt, arg1, arg2, arg3);
  1284. #endif
  1285. }
  1286.  
  1287. /* Generate a log entry, with 'E'rror flagging (non-daemon mode), time-stamp,
  1288. ** and newsgroup name.
  1289. */
  1290. /*VARARGS1*/
  1291. void
  1292. log_error(fmt, arg1, arg2, arg3)
  1293. char *fmt;
  1294. long arg1;
  1295. long arg2;
  1296. long arg3;
  1297. {
  1298.     char fmtbuf[256];
  1299.  
  1300.     sprintf(fmtbuf, "%s: %s", line, fmt);
  1301. #ifndef USESYSLOG
  1302.     log_entry(fmtbuf, arg1, arg2, arg3);
  1303. #else
  1304.     syslog(LOG_NOTICE, fmtbuf, arg1, arg2, arg3);
  1305. #endif
  1306.     if (*fmt == '*') {
  1307.     grevious_error = TRUE;
  1308.     if (!daemon_delay) {
  1309.         putchar('E');
  1310.     }
  1311.     }
  1312.     else {
  1313.     if (!daemon_delay) {
  1314.         putchar('e');
  1315.     }
  1316.     }
  1317. }
  1318.  
  1319. #ifdef MYCHSIZE
  1320.     /* code courtesy of William Kucharski */
  1321.  
  1322. int
  1323. chsize(fd, length)
  1324. int fd;            /* file descriptor */
  1325. off_t length;        /* length to set file to */
  1326. {
  1327.     extern long lseek();
  1328.     struct flock fl;
  1329.  
  1330.     if (fstat(fd, &filestat) < 0) {
  1331.     return -1;
  1332.     }
  1333.     if (filestat.st_size < length) {    /* extend file length */
  1334.     /* Write a 0 byte at the end. */
  1335.     if (lseek(fd, length - 1, 0) < 0
  1336.      || write(fd, "", 1) != 1) {
  1337.         return -1;
  1338.     }
  1339.     } else {
  1340.     /* Truncate file at length. */
  1341.     fl.l_whence = 0;
  1342.     fl.l_len = 0;
  1343.     fl.l_start = length;
  1344.     fl.l_type = F_WRLCK;        /* write lock on file space */
  1345.  
  1346.     /*
  1347.     ** This relies on the UNDOCUMENTED F_FREESP argument to
  1348.     ** fcntl(2), which truncates the file so that it ends at the
  1349.     ** position indicated by fl.l_start.
  1350.     **
  1351.     ** Will minor miracles never cease?
  1352.     */
  1353.     if (fcntl(fd, F_FREESP, &fl) < 0) {
  1354.         return -1;
  1355.     }
  1356.     }
  1357.     return 0;
  1358. }
  1359. #endif
  1360.  
  1361. #ifdef MVTRUNC
  1362. int
  1363. mvtrunc(filename, truncate_len)
  1364. char *filename;
  1365. long truncate_len;
  1366. {
  1367.     FILE *fp_in, *fp_out;
  1368.  
  1369.     sprintf(line, "%s.new", filename);
  1370.     if ((fp_out = fopen(line, "w")) == Nullfp) {
  1371.     log_entry("Tried to create active2.new.\n");
  1372.     return -1;
  1373.     }
  1374.     if ((fp_in = fopen(filename, "r")) == Nullfp) {
  1375.     fclose(fp_out);
  1376.     unlink(line);
  1377.     log_entry("Tried to re-open the active2 file.\n");
  1378.     return -1;
  1379.     }
  1380.     while (ftell(fp_out) < truncate_len) {
  1381.     if (!fgets(buf, sizeof buf, fp_in)) {
  1382.         break;
  1383.     }
  1384.     fputs(buf, fp_out);
  1385.     }
  1386.     sprintf(buf, "%s.old", filename);
  1387.     rename(filename, buf);
  1388.     rename(line, filename);
  1389.     fclose(fp_in);
  1390.     fclose(fp_out);
  1391.  
  1392.     return 0;
  1393. }
  1394. #endif
  1395.  
  1396. #ifndef RENAME
  1397. int
  1398. rename(old, new)
  1399. char    *old, *new;
  1400. {
  1401.     struct stat st;
  1402.  
  1403.     if (stat(old, &st) == -1) {
  1404.     return -1;
  1405.     }
  1406.     if (unlink(new) == -1 && errno != ENOENT) {
  1407.     return -1;
  1408.     }
  1409.     if (link(old, new) == -1) {
  1410.     return -1;
  1411.     }
  1412.     if (unlink(old) == -1) {
  1413.     int e = errno;
  1414.     (void) unlink(new);
  1415.     errno = e;
  1416.     return -1;
  1417.     }
  1418.     return 0;
  1419. }
  1420. #endif /*RENAME*/
  1421.  
  1422. #ifdef CHECKLOAD
  1423.  
  1424. #define FREQCHECK    300
  1425. #define SLPLOAD        300
  1426. #define UPTIME        "/usr/ucb/uptime"
  1427. #define TOOHIGH        5
  1428.  
  1429. checkload()
  1430. {
  1431.     static long lastcheck = 0;
  1432.     long time(), i;
  1433.     FILE *pp;
  1434.     char buf[BUFSIZ];
  1435.     register char *cp;
  1436.     char *strrchr();
  1437.  
  1438.     i = time(Null(time_t*));
  1439.     if ((i - lastcheck) < FREQCHECK) {
  1440.     return;
  1441.     }
  1442.     lastcheck = i;
  1443.  
  1444. again:
  1445.     if ((pp = popen(UPTIME, "r")) == NULL) {
  1446.     return;
  1447.     }
  1448.     if (fgets(buf, BUFSIZ, pp) == NULL) {
  1449.     pclose(pp);
  1450.     return;
  1451.     }
  1452.     pclose(pp);
  1453.     if ((cp = strrchr(buf, ':')) == NULL) {
  1454.     return;
  1455.     }
  1456.     if (atoi(cp + 2) >= TOOHIGH) {
  1457.     sleep(SLPLOAD);
  1458.     goto again;
  1459.     } else {
  1460.     return;
  1461.     }
  1462. }
  1463. #endif
  1464.