home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / languages / perl / scripts-osu / nntptap < prev    next >
Encoding:
Internet Message Format  |  1990-03-13  |  8.6 KB

  1. Path: tut.cis.ohio-state.edu!zaphod.mps.ohio-state.edu!brutus.cs.uiuc.edu!jarthur!elroy.jpl.nasa.gov!decwrl!orc!inews!iwarp.intel.com!news
  2. From: merlyn@iwarp.intel.com (Randal Schwartz)
  3. Newsgroups: news.software.nntp,comp.lang.perl
  4. Subject: nntptap [Perl] (was Re: improved nntpxfer)
  5. Message-ID: <1990Mar15.175737.8773@iwarp.intel.com>
  6. Date: 15 Mar 90 17:57:37 GMT
  7. References: <X?#$?!*@b-tech.uucp> <1990Mar14.231020.5784@smurf.sub.org>
  8. Sender: news@iwarp.intel.com
  9. Reply-To: merlyn@iwarp.intel.com (Randal Schwartz)
  10. Organization: Stonehenge; netaccess via Intel, Beaverton, Oregon, USA
  11. Lines: 243
  12. Xref: tut.cis.ohio-state.edu news.software.nntp:636 comp.lang.perl:727
  13. In-Reply-To: urlichs@smurf.sub.org (Matthias Urlichs)
  14.  
  15. In article <1990Mar14.231020.5784@smurf.sub.org>, urlichs@smurf (Matthias Urlichs) writes:
  16. | Another improvement is to open two NNTP channels to your favorite server. On
  17. | one, you do your NEWNEWS, and the other is used to fetch articles as soon as
  18. | their IDs come in over the first channel.
  19. | This is necessary on some low-speed Internet links like ours (which frequently
  20. | makes nntpd time out, drops connections, and other fun stuff) and basically
  21. | enabled us to get 24 hours of Usenet traffic in 14 hours instead of 30.
  22. | I'd like to convert this to a somewhat better C programming style before
  23. | letting the rest of the world see it, though...
  24.  
  25. I've been using a Perl program that I call 'nntptap' that does exactly
  26. *that* for the last few months.  (In fact, my only feeds have been
  27. exclusively through this program.)
  28.  
  29. nntptap opens NNTP servers on both sides (two on the sender, and one
  30. on the receiver), so it could be used in place of nntpxmit as well.
  31.  
  32. If asked, it maintains a timestamp file of your choosing, and will
  33. record the beginning of the most recent successful transfer as the
  34. mtime of that file.  On subsequent transfers, the NEWNEWS command is
  35. generated accordingly.  If you don't use a timestamp, the default
  36. period is '42 days', which appears to be plenty big to get all the
  37. news on the source server that you don't have already. :-)
  38.  
  39. Here's what it looks like in my crontab for news:
  40.  
  41. 42 * * * * nice /usr/lib/newsbin/nntptap2 -v1 -finews -s/usr/lib/news/stamp.inews >>/usr/lib/news/log.inews 2>&1
  42. 52 * * * * nice /usr/lib/newsbin/nntptap2 -v1 -fomepd -s/usr/lib/news/stamp.omepd >>/usr/lib/news/log.omepd 2>&1
  43. 12 0,2,4,6,8,10,12,14,16,18,20,22 * * * nice /usr/lib/newsbin/nntptap2 -v1 -f129.189.192.20 -s/usr/lib/news/stamp.orc >>/usr/lib/news/log.orc 2>&1
  44. 12 1,3,5,7,9,11,13,15,17,19,21,23 * * * nice /usr/lib/newsbin/nntptap2 -v1 -f129.95.40.2 -s/usr/lib/news/stamp.ogicse >>/usr/lib/news/log.ogicse 2>&1
  45.  
  46. It currently doesn't test distributions, just newsgroups, so it is
  47. prone to distribution leaks.  In practice, I have not found this to be
  48. a problem, so I am not solving it (yet!).
  49.  
  50. I had problems with the server timing out, so I have a watchdog
  51. process that kills the transfer if it starts taking too long.  This
  52. has worked so far, but if you have a better solution, lemme know.
  53.  
  54. If you start using this, lemme know, and I'll throw you on a mailing
  55. list for any update announcements.
  56.  
  57. ################################################## snip here
  58. #!/local/merlyn/bin/perl
  59.  
  60. $ZERO = $0;
  61.  
  62. sub usage {
  63.     die join("\n",@_) .
  64.     "\nusage: $ZERO [-f fromhost] [-t tohost] [-s stampfile] [-v verboselevel] [-g groups] [-w watchdogseconds]\n";
  65. }
  66.  
  67. do 'getopt.pl' || die "Cannot do getopts.pl ($!)";
  68.  
  69. &Getopt('ftsvgw');
  70.  
  71. &usage("extra arguments: @ARGV") if $#ARGV > -1;
  72.  
  73. chop($thishost = `hostname`);
  74. ## defaults:
  75. $fromhost = defined $opt_f ? $opt_f : $thishost;
  76. $tohost = defined $opt_t ? $opt_t : $thishost;
  77. $stampfile = $opt_s; # null string means no stamp
  78. $verbose = defined $opt_v ? $opt_v : 0;
  79. $sub = defined $opt_g ? $opt_g : "comp,news,sci,rec,misc,soc,talk,to,alt,gnu,intel,pnw,or,pdx";
  80. $watchdogseconds = defined $opt_w ? $opt_w : 4*60*60; # 4 hour default
  81.  
  82. ## verbose codes:
  83. ## 0 = only summary
  84. ## 1 = single letter progress
  85. ## 2 = noisy progress
  86. ## 3 = handshaking too
  87.  
  88. &usage("fromhost = tohost?") if $fromhost eq $tohost;
  89.  
  90. $sockaddr = 'S n a4 x8';
  91. @x = getprotobyname('tcp'); $proto = $x[2];
  92. @x = getservbyname('nntp','tcp'); $port = $x[2];
  93.  
  94. sub hosttoaddr {
  95.     local($hostname) = @_;
  96.     local(@x);
  97.     if ($hostname =~ /^(\d+)+\.(\d+)\.(\d+)\.(\d+)$/) {
  98.         pack('C4', $1, $2, $3, $4);
  99.     } else {
  100.         @x = gethostbyname($hostname);
  101.         die "gethostbyname: $hostname ($!)" if $#x < 0;
  102.         $x[4];
  103.     }
  104. }
  105.  
  106. $toaddr = &hosttoaddr($tohost);
  107. $fromaddr = &hosttoaddr($fromhost);
  108. $thisaddr = &hosttoaddr($thishost);
  109.  
  110. $thisproc = pack($sockaddr, 2, 0, $thisaddr);
  111. $tonntp = pack($sockaddr, 2, $port, $toaddr);
  112. $fromnntp = pack($sockaddr, 2, $port, $fromaddr);
  113.  
  114. $| = 1;
  115.  
  116. $mtime = ($stampfile && (@x = stat($stampfile)) && $x[9]) || time-86400*42;
  117. @x = gmtime($mtime-3600); # one hour overlap
  118. $timestamp = sprintf("%02d%02d%02d %02d%02d%02d GMT",
  119.         $x[5],$x[4]+1,$x[3],$x[2],$x[1],$x[0]);
  120.  
  121. sub setup {
  122.     local($FH) = shift;
  123.     local($fromorto) = shift ? $fromnntp : $tonntp;
  124.     local($oldfh);
  125.     socket($FH, 2, 1, $proto) || die "$FH socket: $!";
  126.     bind($FH, $thisproc) || die "$FH bind: $!";
  127.     connect($FH, $fromorto) || die "$FH connect: $!";
  128.     $oldfh = select($FH); $| = 1; select($oldfh);
  129.     (($_ = &get($FH)) =~ /^2/) || die "got $_ during greeting $FH";
  130.     &put($FH,"SLAVE");
  131.     (($_ = &get($FH)) =~ /^2/) || die "got $_ during slave $FH";
  132. }
  133.  
  134. sub put {
  135.     local($FH) = shift;
  136.     local($what) = shift;
  137.     print $FH "$what\n";
  138.     print "$FH >>> $what\n" if $verbose >= 3;
  139.     $what;
  140. }
  141.  
  142. sub get {
  143.     local($FH) = shift;
  144.     local($what);
  145.     $what = <$FH>;
  146.     $what =~ s/\015//;
  147.     $what =~ s/\n//;
  148.     print "$FH >> $what\n" if $verbose >= 3;
  149.     $what;
  150. }
  151.  
  152. $starttime = time;
  153. @x = localtime($starttime);
  154. printf "%s: begin %02d/%02d/%02d %02d:%02d:%02d\n",
  155.     $fromhost,$x[5],$x[4]+1,$x[3],$x[2],$x[1],$x[0];
  156.  
  157. ## launching the watchdog:
  158.  
  159. unless (fork) {
  160.     $target = $starttime + $watchdogseconds;
  161.     while (time < $target) {
  162.         sleep 120;
  163.         exit 0 if ($ppid = getppid) == 1; # orphaned
  164.     }
  165.     kill 15, $ppid;
  166.     sleep 10;
  167.     kill 9, $ppid;
  168.     exit 1;
  169. }
  170.  
  171. &setup("FI",1); # 'F'rom 'I'ndex: send NEWNEWS, use reply as worklist
  172. &setup("FD",1); # 'F'rom 'D'ata: send ARTICLE to fetch article
  173. &setup("TD",0); # 'T'o 'D'ata: send IHAVE to see if wanted, and to store it
  174.  
  175. ## basic algorithm: start a NEWNEWS going.  As it spits out each article ID,
  176. ## send that down as an IHAVE to the receiver (we lie, because we don't
  177. ## *really* have it... yet).  If the receiver wants it (doesn't say "Got it"),
  178. ## send ARTICLE on the other input channel to get the text, sending it line
  179. ## at a time to receiver.  If the sender balks on the ARTICLE (expired or
  180. ## cancelled), send an empty article to the receiver (which it mostly
  181. ## ignores).  Repeat this a zillion times.
  182.  
  183. ($groups = $sub) =~ s/([^,]+)/\1.*/g;
  184. $groups .= ",control";
  185. &put("FI","NEWNEWS $groups $timestamp");
  186. (($_ = &get("FI")) =~ /^2/) || die "got $_ during newnews FI";
  187.  
  188. MAIN: {
  189.     $_ = &get("FI");
  190.     last MAIN if /^\./;
  191.     $art = $_;
  192.     $arts++;
  193.     &put("TD", "IHAVE $art");
  194.     $_ = &get("TD");
  195.     unless (/^3/) {
  196.         $rejects++;
  197.         print "$fromhost: rejecting $art: $_\n" if $verbose >= 2;
  198.         print "r" if $verbose == 1;
  199.         redo MAIN;
  200.     }
  201.     &put("FD", "ARTICLE $art");
  202.     $_ = &get("FD");
  203.     unless (/^2/) {
  204.         # they didn't have what they said they had (expired/cancelled)
  205.         &put("TD","."); # terminate the article, sorry!
  206.         $aborts++;
  207.         print "$fromhost: aborting $art: $_\n" if $verbose >= 2;
  208.         print "a" if $verbose == 1;
  209.         &get("TD"); # ignore return
  210.         redo MAIN;
  211.     }
  212.     print "$fromhost: transferring $art\n" if $verbose >= 2;
  213.     print "t" if $verbose == 1;
  214.     INNER: {
  215.         $_ = &get("FD");
  216.         last INNER if /^\.$/;
  217.         # an initial period is doubled, but that's the way we want it
  218.         &put("TD",$_);
  219.         redo INNER;
  220.     }
  221.     &put("TD",".");
  222.     $_ = &get("TD");
  223.     unless(/^2/) {
  224.         $errors++;
  225.         print "$fromhost: error at $art: $_" if $verbose >= 2;
  226.         print "e" if $verbose == 1;
  227.     }
  228.     redo MAIN;
  229. }
  230.  
  231. print "\n" if $verbose == 1;
  232. printf "%s: stats %d offered %d accepted %d rejected %d aborted %d failed\n",
  233.     $fromhost, $arts, $arts-$rejects-$aborts-$errors, $rejects,
  234.     $aborts, $errs;
  235. @x = times;
  236. printf "%s: times user %.1f system %.1f elapsed %.1f\n",
  237.     $fromhost, $x[0], $x[1], time-$starttime;
  238. exit 2 if $errs;
  239. if ($stampfile) {
  240.     unless (-e $stampfile) {
  241.         open(S,">$stampfile") || die "Cannot create $stampfile ($!)";
  242.         close(S);
  243.     }
  244.     utime $starttime, $starttime, $stampfile ||
  245.         die "Cannot utime $stampfile ($!)";
  246. }
  247. exit 0;
  248.  
  249. ################################################## snip here
  250.  
  251. Just another Perl hacker and Usenet administrator,
  252. -- 
  253. /=Randal L. Schwartz, Stonehenge Consulting Services (503)777-0095 ==========\
  254. | on contract to Intel's iWarp project, Beaverton, Oregon, USA, Sol III      |
  255. | merlyn@iwarp.intel.com ...!any-MX-mailer-like-uunet!iwarp.intel.com!merlyn |
  256. \=Cute Quote: "Welcome to Portland, Oregon, home of the California Raisins!"=/
  257.  
  258.