home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / net / news.batcher < prev    next >
Internet Message Format  |  1986-06-20  |  16KB

  1. From clewis@mnetor.UUCP (Chris Lewis) Thu Jun 19 16:37:07 1986
  2. Path: seismo!harvard!think!nike!ucbcad!ucbvax!hplabs!motsj1!mnetor!clewis
  3. From: clewis@mnetor.UUCP (Chris Lewis)
  4. Newsgroups: net.sources
  5. Subject: News Admins: Never blow a spool area again!
  6. Message-ID: <3551@mnetor.UUCP>
  7. Date: 19 Jun 86 20:37:07 GMT
  8. Organization: Computer X (CANADA) Ltd., Toronto, Ontario, Canada
  9. Lines: 590
  10. Keywords: news batch spool uucp feed
  11.  
  12. #!/bin/sh
  13. echo 'Start of pack.out, part 01 of 01:'
  14. echo 'x - README'
  15. sed 's/^X//' > README << '/'
  16. XNews Admins!
  17. X
  18. XSick of having millions of cron entries, differing [cs7]sendbatch scripts,
  19. Xand blown out spool areas with your downstream feeds?  Well, have I got
  20. Xsomething for you!
  21. X
  22. XThe following program (batcher) solves all of these problems (if your're
  23. Xlucky!).  You install it in your /usr/lib/news along with a batch 
  24. Xcontrol file ("batch.ctrl").  batch.ctrl contains a line per down-stream 
  25. Xsite which contains descriptions on how to send news to that site.
  26. X
  27. XSetup:
  28. X    - modify your "sys" file so that each system you want to control
  29. X      this way has batching turned on.  Eg:
  30. X        mnetor:....:F:/usr/spool/batch/mnetor
  31. X    - add a line for the site into batch.ctrl.  batch.ctrl contains
  32. X      descriptions of each field.
  33. X    - via cron, run batcher setuid news with either:
  34. X        - the name of the system
  35. X        - a "class" (which'll run all systems with that class)
  36. X
  37. XAdvantages:
  38. X    1) Batching instructions for all downstream sites is contained
  39. X       in one file, instead of having to make customized *sendbatches
  40. X       (eg: differing bits in compress, different grades, different
  41. X       batch sizes)
  42. X    2) You can control the size of a batch - tuning for various
  43. X       link qualities
  44. X    3) If you have 4.3 BSD UUCP - you can completely eliminate
  45. X       the possibility of blowing out your spool area.  batch.ctrl
  46. X       allows you to specify the maximum length the UUCP queue can be
  47. X       for each system.  Batcher will not invoke the real batching 
  48. X       software unless and until the UUCP queue size for that site 
  49. X       is below this limit.  If the queue for a specific system
  50. X       grows above this length, the batch file will be left untouched
  51. X       and will only be processed when the downstream manages to
  52. X       bring the queue size back down below the limit.
  53. X
  54. X       This is done by "popen("uuq -l -s<sitename>", "r")" to
  55. X       find out the size of the queue for the site.  Non 4.3 UUCP
  56. X       can probably write their own function to scan the 
  57. X       /usr/spool/uucp's "C." files to figger out how big the queue
  58. X       is for a specific system.
  59. X
  60. X       If you don't have a way to determine queue size, undef "UUQ"
  61. X       in batcher.c.  (4) below will alleviate the lack of this
  62. X       function, but you may have a network clog if one of your
  63. X       downstreams dies and uses up the rest of the spool area.
  64. X
  65. X    4) If you are running on a 4.x BSD system, batcher will also
  66. X       not invoke the real batching software if the amount of
  67. X       space in the UUCP spool area is below 5 Mb.  This is done
  68. X       by doing a "df /usr/spool/uucp", which I believe only
  69. X       works on 4.2 or 4.3 BSD.  Kludging this for systems
  70. X       where "df <file>" doesn't work shouldn't be too hard.
  71. X       If you can't do this, undef "DFABLE"
  72. X
  73. X    Note: the checks for (3) and (4) are done after each batch is
  74. X    created.  Using uuq and df is kinda slow, but it is swamped
  75. X    by the execution of the batching itself.
  76. X
  77. X    I'd recommend looking at the first bit of batcher.c before running
  78. X    the makefile (SV make only, you may have to fudge a bit for BSD).
  79. X
  80. XThis software has made an incredible difference on this site.
  81. XWe give full feeds to 7 sites, plus smaller ones to about another
  82. X10.  Until this was installed, we had to disable batching to
  83. Xone site or another every second day.  And we blew spool areas on 
  84. Xweekends.  We haven't had to touch *anything* since this was installed.  
  85. XEven when sites have been unreachable for weeks on end.
  86. X
  87. XI'm sorry I haven't been able to write a better set of documentation,
  88. Xor send it off to mod.sources, but tomorrow is my last day here, and
  89. XI may not be able to get back on the net for a while.
  90. X
  91. XWorks with 2.10.2 and 2.10.3 news.
  92. /
  93. echo 'x - batch.ctrl'
  94. sed 's/^X//' > batch.ctrl << '/'
  95. X#    Example batch.ctrl file.
  96. X#    Description:
  97. X#    1st field is name.
  98. X#    2nd field is "class"; "batcher -c<C>" will do all those
  99. X#        systems with class <C>.  Default is "z".
  100. X#    3rd field is "compress line".
  101. X#        Default is no compress
  102. X#        placeing something here uses this as an intermediate
  103. X#        pipe.  If you use compress, with no uux field, the
  104. X#        default is uux .... !cunbatch
  105. X#        You can throw in other compress arguments here.
  106. X#    4th field is uux flags.  default is: -r -z -gd
  107. X#        (do not start uucico, only remote acknowledge of failures,
  108. X#        grade D)
  109. X#    5th field is size of batches.  Default 100K.
  110. X#    6th field is max queue size.  Default 500K
  111. X#    7th field: complete uux line replacement.
  112. X#    default: "uux - <uuxflags> nodename!<unbatch|cunbatch>"
  113. X#
  114. X#    If a name does not exist, batcher won't send a batch.
  115. X#
  116. X#    Normal news feeds:
  117. Xgenat::compress -C::::
  118. Xutcs::compress -C::::
  119. Xradha::compress::::
  120. Xtoram::compress:-n -r -gd:::
  121. Xyetti:z:compress -C::::
  122. Xlsuc::compress -C -b 13::::
  123. Xmicomvax::::::
  124. X#    new news feed coming up
  125. Xsyntron::compress -C::::
  126. X#    Mot news feeds:
  127. Xmotsj1:A::-n -r %gd222Pegt2A22%f %r %gd222P#cP fews feeds2P#cped2A22%f %gB222Pcp`q2A22%f %gB222Pcpsea2A22%f %gB222Pcpm{o:I:komxzm{s -C:-n -gB:::
  128. Xcxsch:A:::20000::mail cxsch!ewa
  129. Xcxtex:A:::20000::mail cxtex!cxmail
  130. Xcxphx:A:::20000::mail cxphx!nms
  131. Xcxmd:A:::20000::mail cxmd!joe
  132. Xtest:A:::::mail clewis
  133. /
  134. echo 'x - batcher.c'
  135. sed 's/^X//' > batcher.c << '/'
  136. X/*    Chris Lewis, June 1986    */
  137. X#include <stdio.h>
  138. X#if    defined(BSD4_2) || defined(BSD4_1C)
  139. X#include <strings.h>
  140. X#else
  141. X#include <string.h>
  142. X#endif
  143. Xextern char *malloc();
  144. Xextern void free();
  145. X
  146. X#define    NUMFLDS    7
  147. X#define    NAME    0
  148. X#define    CLASS    1
  149. X#define    COMPRESS 2
  150. X#define    UUXFLAGS 3
  151. X#define    SIZE    4
  152. X#define    QSIZE    5
  153. X#define    UUX    6
  154. X
  155. X
  156. X#define    UUQ    /* define this if you have BSD 4.3 uuq utility */
  157. X#define    DFABLE    /* define this if "df /usr/spool/uucp" works on your system */
  158. X
  159. Xint    fldlen[NUMFLDS] = {10, 2, 20, 10, 7, 10, 30};
  160. X
  161. Xstruct desc {
  162. X    char         *flds[NUMFLDS];
  163. X    struct desc    *next;
  164. X} *head = (struct desc *) NULL, 
  165. X  *dptr = (struct desc *) NULL;
  166. X
  167. X#if    defined(BSD4_2) || defined(BSD4_1C)
  168. X#define    strchr    index
  169. X#endif
  170. X
  171. X#ifdef    DEBUG
  172. X#define    BATCHCTRL    "batch.ctrl"
  173. X#else
  174. X#define    BATCHCTRL    "/usr/lib/news/batch.ctrl"
  175. X#endif
  176. X
  177. X#define    BATCH    "/usr/spool/batch"
  178. X#define    SBATCH    "/usr/lib/news/batch"
  179. X
  180. Xstruct desc *getflds();
  181. X
  182. Xint    verbose = 0;
  183. Xint    class;
  184. Xint    spoolok = 1;
  185. X
  186. Xlong    spoollim = 5000000;
  187. X
  188. X/*
  189. X *    main:
  190. X *        - process arguments
  191. X *        - read control file
  192. X *        - for each system selected, see if there is any work,
  193. X *            if so, go try to do it.
  194. X */
  195. X
  196. Xmain(argc, argv)
  197. Xint    argc;
  198. Xchar    **argv; {
  199. X    register char *p;
  200. X    register struct desc *curptr;
  201. X    argc--; argv++;
  202. X    for (;argc > 0 && **argv == '-'; argv++) {
  203. X        for (p = (*argv)+1; *p; p++)
  204. X            switch(*p) {
  205. X                case 'v':
  206. X                    verbose = 1;
  207. X                    break;
  208. X                case 'c':
  209. X                    class = *(p+1);
  210. X                    if (class == 0)
  211. X                        class = 'z';
  212. X                    else
  213. X                        p++;
  214. X                    break;
  215. X                default:
  216. X                    fprintf(stderr, "batcher: Bad arg %s\n", *argv);
  217. X                    exit(1);
  218. X            }
  219. X    }
  220. X    readctrl();
  221. X    if (!checkspool()) {
  222. X        exit(0);
  223. X    }
  224. X    if (verbose)
  225. X        dumpctrl();
  226. X    if (class) {
  227. X        for (curptr = head; curptr && spoolok; curptr = curptr->next) {
  228. X            if (*(curptr->flds[CLASS]) == class &&
  229. X                (chkbatch(curptr->flds[NAME], "") || 
  230. X                chkbatch(curptr->flds[NAME], ".work")) &&
  231. X                (spoolok = checkspool()))
  232. X                doit(curptr->flds[NAME]);
  233. X        }
  234. X    }
  235. X    while(*argv && spoolok) {
  236. X        if (chkbatch(*argv, "") || chkbatch(*argv, ".work")) {
  237. X            doit(*argv);
  238. X        }
  239. X        argv++;
  240. X    }
  241. X    exit(0);
  242. X
  243. X}
  244. X
  245. X/*
  246. X *    readctrl:
  247. X *
  248. X *    Each line in the batch.ctrl file contains NUMFLDS colon-separated
  249. X *    parameters.  This function reads each line, and calls getflds
  250. X *    to separate out the parameters.  If getflds returns a system descriptor
  251. X *    it is linked into the list of system descriptors.
  252. X */
  253. X
  254. Xreadctrl() {
  255. X    char    buf[BUFSIZ];
  256. X    register char    *p;
  257. X    register struct desc *ptr;
  258. X    struct desc *getflds();
  259. X    register FILE    *ctrl = fopen(BATCHCTRL, "r");
  260. X    if (!ctrl) {
  261. X        fprintf(stderr, "batcher: could not open %s file\n",
  262. X            BATCHCTRL);
  263. X        exit(1);
  264. X    }
  265. X    while (fgets(buf, sizeof(buf), ctrl)) {
  266. X        if (buf[0] == '#')
  267. X            continue;
  268. X        p = buf + strlen(buf) - 1;
  269. X        if (*p == '\n')
  270. X            *p = '\0';
  271. X        if ((ptr = getflds(buf)) && processctrl(ptr)) {
  272. X            if (!head)
  273. X                head = dptr = ptr;
  274. X            else {
  275. X                dptr->next = ptr;
  276. X                dptr = ptr;
  277. X            }
  278. X            ptr->next = (struct desc *) NULL;
  279. X        }
  280. X    }
  281. X    fclose(ctrl);
  282. X}
  283. X
  284. X/*
  285. X *    dumpctrl:
  286. X *
  287. X *    If verbose is on, dump the tables
  288. X */
  289. X
  290. Xdumpctrl() {
  291. X    register struct desc *p;
  292. X    register int i;
  293. X    for (p = head; p; p = p->next) {
  294. X        for (i = 0; i < NUMFLDS; i++)
  295. X            printf("%-*s", fldlen[i], p->flds[i]);
  296. X        printf("\n");
  297. X    }
  298. X}
  299. X
  300. Xchar *strsave();
  301. X
  302. X/*
  303. X *    getflds:
  304. X *
  305. X *    This routine parses a single line from the batch.ctrl file,
  306. X *    and if successfully parsed and checked out, returns a system
  307. X *    descriptor pointer
  308. X */
  309. X
  310. Xstruct desc *
  311. Xgetflds(buf)
  312. Xchar    *buf; {
  313. X    register int cnt;
  314. X    char b2[100];
  315. X    char *curp, *p;
  316. X    int    len;
  317. X    struct desc *dptr;
  318. X    dptr = (struct desc *) malloc(sizeof (struct desc));
  319. X    if (!dptr) {
  320. X        fprintf(stderr, "batcher: Cannot malloc\n");
  321. X        exit(1);
  322. X    }
  323. X    curp = buf;
  324. X    for (cnt = 0; cnt < NUMFLDS; cnt++) {
  325. X        if (cnt == (NUMFLDS - 1)) {
  326. X            if (strchr(curp, ':')) {
  327. X                fprintf(stderr, "batcher: too many colons: %s\n",
  328. X                    buf);
  329. X                free(dptr);
  330. X                return(NULL);
  331. X            }
  332. X            p = curp + strlen(curp);
  333. X        } else
  334. X            p = strchr(curp, ':');
  335. X        if (p == NULL) {
  336. X            fprintf(stderr, "batcher: invalid format (%d): %s\n", 
  337. X                cnt, buf);
  338. X            free(dptr);
  339. X            return(NULL);
  340. X        }
  341. X        len = p - curp;
  342. X        if (len >= fldlen[cnt]) {
  343. X            fprintf(stderr, "batcher: field %d too long: %s\n",
  344. X                cnt+1, buf);
  345. X            free(dptr);
  346. X            return(NULL);
  347. X        }
  348. X        if (!(dptr->flds[cnt] = malloc(len + 1))) {
  349. X            fprintf(stderr, "batcher: cannot malloc\n");
  350. X            exit(1);
  351. X        }
  352. X        strncpy(dptr->flds[cnt], curp, len);
  353. X        dptr->flds[cnt][len] = '\0';
  354. X        curp = p + 1;
  355. X    }
  356. X    return(dptr);
  357. X}
  358. X
  359. X/*
  360. X *    strsave:
  361. X *    returns pointer to malloc'd copy of argument
  362. X */
  363. Xchar *
  364. Xstrsave(s)
  365. Xchar    *s; {
  366. X    register char *p = malloc(strlen(s) + 1);
  367. X    if (!p) {
  368. X        fprintf(stderr, "batcher: cannot malloc\n");
  369. X        exit(1);
  370. X    }
  371. X    strcpy(p, s);
  372. X    return(p);
  373. X}
  374. X
  375. X/*
  376. X *    chkbatch:
  377. X *
  378. X *    return 1 if a batcher work file <batchdir>/<name><type> exists.
  379. X */
  380. X
  381. Xchkbatch(name, type)
  382. Xchar    *name;
  383. Xchar    *type; {
  384. X    char batch[BUFSIZ];
  385. X    sprintf(batch, "%s/%s%s", BATCH, name, type);
  386. X    if (access(batch, 04) == 0)
  387. X        return(1);
  388. X    else
  389. X        return(0);
  390. X}
  391. X
  392. X/*
  393. X *    doit:
  394. X *
  395. X *    This routine is called with the name of the system that has
  396. X *    been determined to have work for it.  The system is searched
  397. X *    for in the system descriptors.  If found, a "system" line
  398. X *    is contructed from the tables, and executed if system has not
  399. X *    exceeded it's UUCP queue limit.
  400. X */
  401. X
  402. Xdoit(name)
  403. Xchar    *name; {
  404. X    char    cmdbuf[BUFSIZ];
  405. X    int    rc;
  406. X    long    queuesize;
  407. X    long    checkqueue(), checkspool();
  408. X    long    queuemax;
  409. X    register struct desc *ptr;
  410. X    if (verbose)
  411. X        printf("batcher: doing %s\n", name);
  412. X    for (ptr = head; ptr; ptr = ptr->next)
  413. X        if (!strcmp(ptr->flds[NAME], name)) {
  414. X            /*    form the command line for batching    */
  415. X            sprintf(cmdbuf, "%s %s/%s %s",
  416. X                SBATCH, BATCH, name, ptr->flds[SIZE]);
  417. X            if (*(ptr->flds[COMPRESS]))
  418. X                sprintf(cmdbuf + strlen(cmdbuf), "|%s",
  419. X                    ptr->flds[COMPRESS]);
  420. X            /*    Find the queue limit    */
  421. X            sprintf(cmdbuf + strlen(cmdbuf), "|%s", ptr->flds[UUX]);
  422. X            if (1 != sscanf(ptr->flds[QSIZE], "%ld", &queuemax)) {
  423. X                fprintf(stderr, "batcher: bad qmax field: %s\n",
  424. X                    ptr->flds[QSIZE]);
  425. X                return;
  426. X            }
  427. X            queuesize = checkqueue(ptr->flds[NAME]);
  428. X            rc = 0;
  429. X            /*    While we haven't exceeded the queue limit &
  430. X                there's work to do, issue the command */
  431. X            while (queuesize < queuemax && !rc && 
  432. X                (chkbatch(ptr->flds[NAME], "") || 
  433. X                   chkbatch(ptr->flds[NAME], ".work")) &&
  434. X                   (spoolok = checkspool())) {
  435. X#ifdef    DEBUG
  436. X                printf("batcher: cmd: %s\n", cmdbuf);
  437. X                rc = 1;
  438. X#else
  439. X                rc = system(cmdbuf);
  440. X                queuesize = checkqueue(ptr->flds[NAME]);
  441. X#endif
  442. X            }
  443. X            return;
  444. X        }
  445. X    fprintf(stderr, "batcher: no control line for %s\n", name);
  446. X}
  447. X
  448. X/*
  449. X *    processctrl:
  450. X *
  451. X *    Check validity of batch.ctrl entries and supply defaults.
  452. X */
  453. Xprocessctrl(ptr)
  454. Xstruct    desc *ptr; {
  455. X    char    buf[100];
  456. X    register char    *p, *uuxflags;
  457. X    if (!ptr) return;
  458. X    if (strlen(ptr->flds[NAME]) == 0) {
  459. X        fprintf(stderr, "batcher: null system name\n");
  460. X        free(ptr);
  461. X        return(0);
  462. X    }
  463. X
  464. X    if (strlen(ptr->flds[QSIZE]) == 0) {
  465. X        free(ptr->flds[QSIZE]);
  466. X        ptr->flds[QSIZE] = strsave("500000");
  467. X    }
  468. X
  469. X    if (strlen(ptr->flds[CLASS]) == 0) {
  470. X        free(ptr->flds[CLASS]);
  471. X        ptr->flds[CLASS] = strsave("z");
  472. X    }
  473. X
  474. X    if (strlen(ptr->flds[SIZE]) == 0) {
  475. X        free(ptr->flds[SIZE]);
  476. X        ptr->flds[SIZE] = strsave("100000");
  477. X    }
  478. X
  479. X    if (strlen(ptr->flds[UUXFLAGS]) == 0) {
  480. X        free(ptr->flds[UUXFLAGS]);
  481. X        ptr->flds[UUXFLAGS] = strsave("-r -z -gd");
  482. X    }
  483. X
  484. X    if (strlen(ptr->flds[UUX]) == 0) {
  485. X        sprintf(buf, "uux - %s %s!%s", ptr->flds[UUXFLAGS],
  486. X            ptr->flds[NAME], 
  487. X            *ptr->flds[COMPRESS] ? "cunbatch" : "rnews");
  488. X        ptr->flds[UUX] = strsave(buf);
  489. X    }
  490. X    return(1);
  491. X}
  492. X
  493. X/*
  494. X *    checkqueue:
  495. X *
  496. X *    Logically, all this code does is return the size of the UUCP queue
  497. X *    for system "name".
  498. X *    I've taken the easy way out and popen'd "uuq" (4.3 BSD UUCP utility)
  499. X *    to parse the first line, which looks something like this:
  500. X *
  501. X *    <systemname>: <n> jobs, <nnnn> bytes, ....
  502. X *
  503. X *    I merely look for the first comma, and sscanf the number following.
  504. X *    A proper solution would be to dive in and parse the UUCP queues
  505. X *    themselves, but: it's moderately difficult, and it changes from 
  506. X *    system to system.
  507. X */
  508. X
  509. Xlong
  510. Xcheckqueue(name)
  511. Xchar    *name; {
  512. X#ifdef    UUQ
  513. X    char buf[BUFSIZ];
  514. X    long retval;
  515. X    register char *p2;
  516. X    FILE *p, *popen();
  517. X    /* Gross, but the easiest way */
  518. X    sprintf(buf, "uuq -l -s%s", name);
  519. X    p = popen(buf, "r");
  520. X    if (!fgets(buf, sizeof(buf), p)) {
  521. X        return(0);
  522. X    }
  523. X    pclose(p);
  524. X    p2 = strchr(buf, ',');
  525. X    if (p2 && 1 == sscanf(p2+1, "%ld", &retval)) {
  526. X        return(retval);
  527. X    }
  528. X    fprintf(stderr, "batcher: could not interpret %s\n", buf);
  529. X    return(10000000);
  530. X#else
  531. X    return(10000000);
  532. X#endif
  533. X}
  534. X
  535. X/*    This function returns the amount of free space on the spool
  536. X    device, this may not work on your system - it reads the
  537. X    second line from a "df /usr/spool/uucp" */
  538. X#define    SPOOL "/usr/spool/uucp"
  539. X
  540. Xcheckspool() {
  541. X#ifdef    DFABLE
  542. X    char buf[100];
  543. X    FILE *p, *popen();
  544. X    long val;
  545. X    sprintf(buf, "df %s", SPOOL);
  546. X    if (!(p = popen(buf, "r"))) {
  547. X        fprintf(stderr, "batcher: couldn't popen %s\n", buf);
  548. X        return(0);
  549. X    }
  550. X    if (!fgets(buf, sizeof(buf), p)) {
  551. X        fprintf(stderr, "batcher: no first line in df\n");
  552. X        return(0);
  553. X    }
  554. X    if (!fgets(buf, sizeof(buf), p)) {
  555. X        fprintf(stderr, "batcher: no second line in df\n");
  556. X        return(0);
  557. X    }
  558. X    if (1 != sscanf(buf, "%*s %*ld %*ld %ld", &val)) {
  559. X        fprintf(stderr, "batcher: couldn't get size from %s\n", buf);
  560. X        return(0);
  561. X    }
  562. X    if (pclose(p)) {
  563. X        fprintf(stderr, "batcher: DF failed\n");
  564. X        return(0);
  565. X    }
  566. X    val *= 1024;
  567. X    if (val < spoollim) {
  568. X        printf("batcher: spool limit exceeded, %ld bytes left\n", val);
  569. X        return(0);
  570. X    } else
  571. X        return(1);
  572. X#else
  573. X    return(1);
  574. X#endif
  575. X}
  576. /
  577. echo 'x - makefile'
  578. sed 's/^X//' > makefile << '/'
  579. XCFLAGS    = -g -DBSD4_2
  580. Xall:    batcher
  581. /
  582. echo 'Part 01 of pack.out complete.'
  583. exit
  584. -- 
  585. Chris Lewis,
  586.  
  587. Until tomorrow:
  588. {pyramid|watmath|utcsri|decvax|allegra|linus|ihnp4}!utzoo!
  589. {utcsri|cbosgd}!utcs!
  590. {yetti|lsuc|genat|mot|oakhill}!
  591. ... mnetor!clewis
  592.  
  593. Monday: utzoo!spectrix!clewis  (I hope)
  594.  
  595. BELL: (416)-475-8980 ext. 321
  596.  
  597.  
  598.