home *** CD-ROM | disk | FTP | other *** search
/ Unix System Administration Handbook 1997 October / usah_oct97.iso / news / cnews.tar / relay / trbatch.c < prev   
C/C++ Source or Header  |  1993-01-14  |  8KB  |  286 lines

  1. /*
  2.  * transmit batch file management (UUNET-sized-site version; master batch files)
  3.  */
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <errno.h>
  7. #include "fixerrno.h"
  8. #include <sys/types.h>
  9. #include "hdbm.h"
  10. #include "hash.h"
  11. #include "libc.h"
  12. #include "news.h"
  13. #include "config.h"    /* for artfile() */
  14. #include "msgs.h"
  15. #include "trbatch.h"
  16.  
  17. /* tunable parameters */
  18. #ifndef MASTERDIR
  19. #define MASTERDIR artfile("out.master")
  20. #endif            /* MASTERDIR */
  21. #ifndef NBFSPERMBF
  22. #define NBFSPERMBF 10    /* # batchfiles per master batchfile (200 for UUNET) */
  23.             /* computable with dup */
  24. #endif            /* NBFSPERMBF */
  25. #ifndef HASHFILSZ
  26. #define HASHFILSZ ((NOPENBFS*NBFSPERMBF)/2)
  27.             /* # of hash buckets for batch file names */
  28. #endif            /* HASHFILSZ */
  29.  
  30. #define MAXSLOTS (NOPENBFS*NBFSPERMBF)
  31.  
  32. /* mapping macros */
  33. #define MBORD(slot)  ((unsigned)(slot) / NBFSPERMBF) /* slot to mbf ordinal */
  34. #define MBSLOT(slot) ((unsigned)(slot) % NBFSPERMBF) /* slot to mbf subslot */
  35.  
  36. /* TODO: dynamically allocate and grow these data structures */
  37. /*
  38.  * Each "batchfile" is actually a master batch file, each of
  39.  * which contains the data needed to create up to NBFSPERMBF batch files
  40.  * (by a separate "exploder" program).  The format is
  41.  *    # optional comment line
  42.  *    <msgid> relative-path size
  43.  *    F out.going/site1/togo
  44.  *    f /usr/spool/news/out.going/site2/togo
  45.  *    n out.nntp/site3
  46.  *    I out.going/site4.wehave/togo
  47.  * where there is a <msgid> line per article and an F, f, n or I line
  48.  * per site getting that article.
  49.  *
  50.  * To avoid splitting multiple sys entries for the same site across
  51.  * master batch files, we need to hash the batch file names down to
  52.  * unique ids, which we then map to a master batch file.
  53.  */
  54. static char ordtombfs[MAXSLOTS];    /* ord -> mb ord map */
  55. static char otomvalid[MAXSLOTS];    /* above mapping valid bits */
  56.  
  57. struct hashfilename {
  58.     unsigned short hf_mbf;        /* master batch file ordinal # */
  59. };
  60. static HASHTABLE *nmordtbl;        /* name -> mbf ordinal mapping */
  61.  
  62.  
  63. /* --- hashing --- */
  64.  
  65. /*
  66.  * search the batchfile cache for "name"; return the corresponding
  67.  * open master batch file, if any.
  68.  */
  69. STATIC struct batchfile *
  70. bfisopen(name)
  71. register char *name;
  72. {
  73.     register struct hashfilename *hf;
  74.  
  75.     if (nmordtbl == NULL)
  76.         return NULL;
  77.     hf = (struct hashfilename *)hashfetch(nmordtbl, name);
  78.     return (hf == NULL? NULL: &batchfile[hf->hf_mbf]);
  79. }
  80.  
  81. STATIC
  82. hfinstall(name, mbford, ord)
  83. char *name;
  84. int mbford, ord;
  85. {
  86.     register struct hashfilename *hf;
  87.  
  88.     if (nmordtbl == NULL)
  89.         nmordtbl = hashcreate(HASHFILSZ, (unsigned (*)())NULL);
  90.     hf = (struct hashfilename *)hashfetch(nmordtbl, name);
  91.     if (hf != NULL)
  92.         return;            /* error: name present */
  93.     /* allocate, append & initialise a new entry */
  94.     hf = (struct hashfilename *)nemalloc(sizeof *hf);
  95.     hf->hf_mbf = mbford;
  96.     if (!hashstore(nmordtbl, strsave(name), (HASHDATUM)hf))
  97.         errunlock("can't store under hash key `%s'", name);
  98.     ordtombfs[ord] = mbford;
  99.     otomvalid[ord] = YES;
  100. }
  101.  
  102.  
  103. /* establish new mapping for a new master batch file */
  104. STATIC struct batchfile *
  105. bfinstall(name, ord)
  106. int ord;
  107. char *name;
  108. {
  109.     register struct batchfile *bf = &batchfile[ord];
  110.  
  111.     if (ordtobfs[ord] != NULL || bf->bf_name != NULL) { /* already in use? */
  112.         errno = 0;
  113.         if (ordtobfs[ord] != NULL)
  114.             errunlock("in bfinstall, ordtobfs[ord] != NULL", "");
  115.         else
  116.             errunlock("in bfinstall, bf->bf_name != NULL", "");
  117.     }
  118.     ordtobfs[ord] = bf;
  119.     bf->bf_name = strsave(name);
  120.     bf->bf_str = NULL;    /* paranoia */
  121. #ifdef notdef
  122.     bf->bf_ref = 0;
  123. #endif
  124.     bf->bf_msgid = NULL;
  125.     bf->bf_lines = FLUSHEVERY;
  126.     return bf;
  127. }
  128.  
  129. /*
  130.  * returns a batchfile, never NULL, corresponding to name and ord.
  131.  * if ord isn't mapped, search the batchfile cache for name;
  132.  * If ord & name are unmapped; find free mbf slot, opening new mbf if needed,
  133.  * and map the name and ord.
  134.  */
  135. STATIC struct batchfile *
  136. bfincache(name, ord)
  137. register char *name;
  138. register int ord;            /* batch file ord */
  139. {
  140.     register struct batchfile *mbf;
  141.  
  142.     if (otomvalid[ord])        /* ord -> mbf mapped? */
  143.         return &batchfile[ordtombfs[ord]];
  144.  
  145.     mbf = bfisopen(name);
  146.     if (mbf == NULL) {        /* name is unmapped? */
  147.         register int slot, mbord;
  148.  
  149.         slot = ord;        /* for stability across runs */
  150.         if (slot >= MAXSLOTS) {
  151.             errno = 0;
  152.             errunlock("in bfincache, slot >= MAXSLOTS", "");
  153.         }
  154.         mbord = MBORD(slot);
  155.         mbf = &batchfile[mbord];
  156.         if (mbf->bf_name == NULL) {
  157.             /* open new mbf */
  158.             register char *mbname;
  159.             char uniq[MAXCOMP+1];
  160.     
  161.             (void) sprintf(uniq, "%d", mbord);
  162.             mbname = str3save(MASTERDIR, SFNDELIM, uniq);
  163.             /* establish new mapping for a new master batch file */
  164.             /* TODO: may want to name an exploder here */
  165.             mbf = bfinstall(mbname, mbord);    /* map mbord -> mbf */
  166.             free(mbname);
  167.         }
  168.     
  169.         /* add this batch file name to the hash chain */
  170.         hfinstall(name, mbord, ord);
  171.     }
  172.  
  173.     /* set up mapping for the future */
  174.     ordtombfs[ord] = mbf - batchfile;    /* mbord */
  175.     otomvalid[ord] = YES;
  176.     /* mapping is now set (ord -> mbf) */
  177.     return mbf;
  178. }
  179.  
  180. /*
  181.  * open "name" for appending, for batch sys entry with ordinal # "ord".
  182.  *
  183.  * if ord is too big, see if any batchfile has been assigned to "name" yet;
  184.  * if not, set up a fake batchfile for temporary use.  if ord is in range,
  185.  * ensure that (name, ord) are mapped to a batchfile.
  186.  *
  187.  * if an attempt to open the batchfile's stream fails, close an arbitrary
  188.  * batchfile stream and retry the open.
  189.  */
  190. struct batchfile *
  191. bfopen(name, ord)
  192. register char *name;
  193. register int ord;
  194. {
  195.     register struct batchfile *bf;
  196.  
  197.     if ((unsigned)ord >= MAXSLOTS) {    /* no mapping possible */
  198.         /* should not get here any more */
  199.         errno = 0;
  200.         errunlock("in bfopen, ord >= MAXSLOTS", "");
  201. #ifdef notdef
  202.         bf = bfisopen(name);
  203.         if (bf == NULL)
  204.             bf = fakebf((FILE *)NULL, name);
  205. #endif
  206.     } else
  207.         bf = bfincache(name, ord);
  208.  
  209.     if (bf->bf_str == NULL) {
  210.         register char *bfname =
  211.             (bf->bf_name != NULL? bf->bf_name: "fake");
  212.  
  213.         /* TODO: may want to use popenclex here for exploders */
  214.         bf->bf_str = fopenclex(bfname, "a");    /* silent try */
  215.         if (bf->bf_str == NULL) {
  216.             if (bfrclose() != ST_OKAY)
  217.                 return NULL;
  218.             /* retry, may bitch */
  219.             bf->bf_str = fopenwclex(bfname, "a");
  220.         }
  221.         if (bf->bf_str != NULL)
  222.             bfsetup(bf);
  223.     }
  224.     return bf;
  225. }
  226.  
  227. /*
  228.  * write filename, message-id or size on batch file "bf".
  229.  * under the 'f' flag, include the size in bytes of the article
  230.  * after "name" to assist the C news batcher.  under the 'n' flag,
  231.  * write the article's message-id.  afterward, flush "bf" in case
  232.  * the machine crashes before the stream is closed.
  233.  * don't check putc return value for portability; use ferror.
  234.  */
  235. int                            /* boolean */
  236. bfappend(bf, flag, batname, artname, msgid, size)
  237. register struct batchfile *bf;
  238. int flag;
  239. char *batname, *artname, *msgid;
  240. long size;
  241. {
  242.     register FILE *bfstr = bf->bf_str;
  243.  
  244.     if (bf->bf_msgid == NULL || !STREQ(bf->bf_msgid, msgid)) {
  245.         /* fresh article on this master batch file */
  246.         if (fflush(bfstr) == EOF)
  247.             return NO;
  248.         nnfree(&bf->bf_msgid);
  249.         bf->bf_msgid = strsave(msgid);
  250.  
  251.         if (fputs(msgid, bfstr) == EOF)
  252.             return NO;
  253.         (void) putc(' ', bfstr);
  254.         if (fputs(artname, bfstr) == EOF)
  255.             return NO;
  256.         (void) putc(' ', bfstr);
  257.         if (fprintf(bfstr, "%ld", size) == EOF)
  258.             return NO;
  259.         (void) putc('\n', bfstr);
  260.     }
  261.  
  262.     /* data for this sys entry */
  263.     (void) putc(flag, bfstr);
  264.     (void) putc(' ', bfstr);
  265. #ifdef FULLBATCHFILENAMES
  266.     if (batname[0] != FNDELIM)
  267.         batname = fullartfile(batname);
  268. #endif
  269.     if (fputs(batname, bfstr) == EOF)
  270.         return NO;
  271.     (void) putc('\n', bfstr);
  272.     if (ferror(bfstr))
  273.         return NO;
  274.     return YES;        
  275. }
  276.  
  277. statust
  278. trrealclose()
  279. {
  280.     register int i;
  281.  
  282.     for (i = 0; i < MAXSLOTS; i++)
  283.         otomvalid[i] = NO;    /* invalidate mappings */
  284.     return bfrealclose();
  285. }
  286.