]> Sergey Matveev's repositories - public-inbox.git/blob - public-inbox-nntpd
read-only NNTP server
[public-inbox.git] / public-inbox-nntpd
1 #!/usr/bin/perl -w
2 # Copyright (C) 2015 all contributors <meta@public-inbox.org>
3 # License: AGPLv3 or later (https://www.gnu.org/licenses/agpl-3.0.txt)
4 use strict;
5 use warnings;
6 require Danga::Socket;
7 use IO::Socket;
8 use Socket qw(SO_KEEPALIVE IPPROTO_TCP TCP_NODELAY);
9 require PublicInbox::NNTP;
10 require PublicInbox::NewsGroup;
11 my $nntpd = PublicInbox::NNTPD->new;
12 my $refresh = sub { $nntpd->refresh_groups };
13
14 my %opts = (
15         LocalAddr => '127.0.0.1:1133',
16         Type => SOCK_STREAM,
17         Proto => 'tcp',
18         Blocking => 0,
19         Reuse => 1,
20         Listen => 1024,
21 );
22 my $s = IO::Socket::INET->new(%opts) or die "Error creating socket: $@\n";
23 $s->sockopt(SO_KEEPALIVE, 1);
24 $s->setsockopt(IPPROTO_TCP, TCP_NODELAY, 1);
25
26 $SIG{PIPE} = 'IGNORE';
27 $SIG{HUP} = $refresh;
28 $refresh->();
29
30 Danga::Socket->AddOtherFds(fileno($s) => sub {
31         while (my $c = $s->accept) {
32                 $c->blocking(0); # no accept4 :<
33                 PublicInbox::NNTP->new($c, $nntpd);
34         }
35 });
36 Danga::Socket->EventLoop();
37
38 package PublicInbox::NNTPD;
39 use strict;
40 use warnings;
41 use fields qw(groups err out);
42
43 sub new {
44         my ($class) = @_;
45         my $self = fields::new($class);
46         $self->{groups} = {};
47         $self;
48 }
49
50 sub refresh_groups {
51         my ($self) = @_;
52         require PublicInbox::Config;
53         my $pi_config = PublicInbox::Config->new;
54         my $new = {};
55         foreach my $k (keys %$pi_config) {
56                 $k =~ /\Apublicinbox\.([^\.]+)\.mainrepo\z/ or next;
57                 my $g = $1;
58                 my $git_dir = $pi_config->{$k};
59                 my $address = $pi_config->{"publicinbox.$g.address"};
60                 my $ng = PublicInbox::NewsGroup->new($g, $git_dir, $address);
61                 my $old_ng = $self->{groups}->{$g};
62
63                 # Reuse the old one if possible since it can hold references
64                 # to valid mm and gcf objects
65                 if ($old_ng) {
66                         $old_ng->update($ng);
67                         $ng = $old_ng;
68                 }
69
70                 # Only valid if Msgmap works
71                 $new->{$g} = $ng if $ng->mm;
72         }
73         # this will destroy old groups that got deleted
74         %{$self->{groups}} = %$new;
75 };
76
77 1;