diff options
Diffstat (limited to 'script')
-rwxr-xr-x | script/lei | 19 | ||||
-rwxr-xr-x | script/public-inbox-cindex | 102 | ||||
-rwxr-xr-x | script/public-inbox-clone | 35 | ||||
-rwxr-xr-x | script/public-inbox-compact | 20 | ||||
-rwxr-xr-x | script/public-inbox-convert | 45 | ||||
-rwxr-xr-x | script/public-inbox-edit | 8 | ||||
-rwxr-xr-x | script/public-inbox-extindex | 2 | ||||
-rwxr-xr-x | script/public-inbox-fetch | 4 | ||||
-rwxr-xr-x | script/public-inbox-index | 10 | ||||
-rwxr-xr-x | script/public-inbox-init | 41 | ||||
-rwxr-xr-x | script/public-inbox-learn | 12 | ||||
-rwxr-xr-x | script/public-inbox-mda | 21 | ||||
-rwxr-xr-x | script/public-inbox-purge | 6 | ||||
-rwxr-xr-x | script/public-inbox-watch | 16 | ||||
-rwxr-xr-x | script/public-inbox-xcpdb | 20 |
15 files changed, 238 insertions, 123 deletions
@@ -2,7 +2,7 @@ # Copyright (C) all contributors <meta@public-inbox.org> # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt> use v5.12; -use Socket qw(AF_UNIX SOCK_SEQPACKET MSG_EOR pack_sockaddr_un); +use Socket qw(AF_UNIX SOCK_SEQPACKET pack_sockaddr_un); use PublicInbox::CmdIPC4; my $narg = 5; my $sock; @@ -92,8 +92,8 @@ my $addr = pack_sockaddr_un($path); socket($sock, AF_UNIX, SOCK_SEQPACKET, 0) or die "socket: $!"; unless (connect($sock, $addr)) { # start the daemon if not started local $ENV{PERL5LIB} = join(':', @INC); - open(my $daemon, '-|', $^X, qw[-MPublicInbox::LEI - -E PublicInbox::LEI::lazy_start(@ARGV)], + open(my $daemon, '-|', $^X, $^W ? ('-w') : (), + qw[-MPublicInbox::LEI -e PublicInbox::LEI::lazy_start(@ARGV)], $path, $! + 0, $narg) or die "popen: $!"; while (<$daemon>) { warn $_ } # EOF when STDERR is redirected close($daemon) or warn <<""; @@ -109,24 +109,21 @@ open my $dh, '<', '.' or die "open(.) $!"; my $buf = join("\0", scalar(@ARGV), @ARGV); while (my ($k, $v) = each %ENV) { $buf .= "\0$k=$v" } $buf .= "\0\0"; -$send_cmd->($sock, [0, 1, 2, fileno($dh)], $buf, MSG_EOR) or die "sendmsg: $!"; -$SIG{TSTP} = sub { send($sock, 'STOP', MSG_EOR); kill 'STOP', $$ }; -$SIG{CONT} = sub { send($sock, 'CONT', MSG_EOR) }; +$send_cmd->($sock, [0, 1, 2, fileno($dh)], $buf, 0) or die "sendmsg: $!"; +$SIG{TSTP} = sub { send($sock, 'STOP', 0); kill 'STOP', $$ }; +$SIG{CONT} = sub { send($sock, 'CONT', 0) }; my $x_it_code = 0; while (1) { my (@fds) = $recv_cmd->($sock, my $buf, 4096 * 33); - if (scalar(@fds) == 1 && !defined($fds[0])) { - next if $!{EINTR}; - die "recvmsg: $!"; - } + die "recvmsg: $!" if scalar(@fds) == 1 && !defined($fds[0]); last if $buf eq ''; if ($buf =~ /\Aexec (.+)\z/) { $exec_cmd->(\@fds, split(/\0/, $1)); } elsif ($buf eq '-WINCH') { kill($buf, @parent); # for MUA } elsif ($buf eq 'umask') { - send($sock, 'u'.pack('V', umask), MSG_EOR) or die "send: $!" + send($sock, 'u'.pack('V', umask), 0) or die "send: $!" } elsif ($buf =~ /\Ax_it ([0-9]+)\z/) { $x_it_code ||= $1 + 0; last; diff --git a/script/public-inbox-cindex b/script/public-inbox-cindex new file mode 100755 index 00000000..dd00623a --- /dev/null +++ b/script/public-inbox-cindex @@ -0,0 +1,102 @@ +#!perl -w +# Copyright (C) all contributors <meta@public-inbox.org> +# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt> +use v5.12; +use Getopt::Long qw(:config gnu_getopt no_ignore_case auto_abbrev); +my $help = <<EOF; # the following should fit w/o scrolling in 80x24 term: +usage: public-inbox-cindex [options] -g GIT_DIR [-g GIT_DIR]... +usage: public-inbox-cindex [options] --project-list=FILE -r PROJECT_ROOT + + Create and update search indices for code repos + + -d EXTDIR use EXTDIR instead of GIT_DIR/public-inbox-cindex + --no-fsync speed up indexing, risk corruption on power outage + -L LEVEL `medium', or `full' (default: medium) + --project-list=FILE use a cgit/gitweb-compatible list of projects + --update | -u update previously-indexed code repos with `-d' + --jobs=NUM set or disable parallelization (NUM=0) + --batch-size=BYTES flush changes to OS after a given number of bytes + --max-size=BYTES do not index commit diffs larger than the given size + --prune prune old repos and commits + --reindex reindex previously indexed repos + --verbose | -v increase verbosity (may be repeated) + +BYTES may use `k', `m', and `g' suffixes (e.g. `10m' for 10 megabytes) +See public-inbox-cindex(1) man page for full documentation. +EOF +my $opt = { fsync => 1, scan => 1 }; # --no-scan is hidden +GetOptions($opt, qw(quiet|q verbose|v+ reindex jobs|j=i fsync|sync! dangerous + indexlevel|index-level|L=s join:s@ + batch_size|batch-size=s max_size|max-size=s + include|I=s@ only=s@ all show:s@ + project-list=s exclude=s@ project-root|r=s + git-dir|g=s@ + sort-parallel=s sort-compress-program=s sort-buffer-size=s + d=s update|u scan! prune dry-run|n C=s@ help|h)) + or die $help; +if ($opt->{help}) { print $help; exit 0 }; +die "--jobs must be >= 0\n" if defined $opt->{jobs} && $opt->{jobs} < 0; +require IO::Handle; +STDOUT->autoflush(1); +STDERR->autoflush(1); +$SIG{USR1} = 'IGNORE'; # to be overridden in cidx_sync +$SIG{PIPE} = 'IGNORE'; +# require lazily to speed up --help +require PublicInbox::Admin; +PublicInbox::Admin::do_chdir(delete $opt->{C}); +my $cfg = $opt->{-pi_cfg} = PublicInbox::Config->new; +my $cidx_dir = $opt->{d}; +PublicInbox::Admin::require_or_die('Xapian'); +PublicInbox::Admin::progress_prepare($opt); +my $env = PublicInbox::Admin::index_prepare($opt, $cfg); +%ENV = (%ENV, %$env) if $env; + +my @git_dirs; +require PublicInbox::CodeSearchIdx; # unstable internal API +if (@ARGV) { + my @g = map { "-g $_" } @ARGV; + die <<EOM; +Specify git directories with `-g' (or --git-dir=): @g +Or use --project-list=... and --project-root=... +EOM +} elsif (defined(my $pl = $opt->{'project-list'})) { + my $pfx = $opt->{'project-root'} // die <<EOM; +PROJECT_ROOT required for --project-list +EOM + $opt->{'git-dir'} and die <<EOM; +--project-list does not accept additional --git-dir directories +(@{$opt->{'git-dir'}}) +EOM + open my $fh, '<', $pl or die "open($pl): $!\n"; + chomp(@git_dirs = <$fh>); + $pfx .= '/'; + $pfx =~ tr!/!/!s; + substr($_, 0, 0, $pfx) for @git_dirs; +} elsif (my $gd = $opt->{'git-dir'}) { + @git_dirs = @$gd; +} elsif (grep defined, @$opt{qw(show update prune)}) { +} else { + warn "No --git-dir= nor --project-list= + --project-root= specified\n"; + die $help; +} + +$_ = PublicInbox::Admin::resolve_git_dir($_) for @git_dirs; +if (defined $cidx_dir) { # external index + die "`%' is not allowed in $cidx_dir\n" if $cidx_dir =~ /\%/; + my $cidx = PublicInbox::CodeSearchIdx->new($cidx_dir, $opt); + @{$cidx->{git_dirs}} = @git_dirs; # may be empty + $cidx->cidx_run; +} elsif (!@git_dirs) { + die $help +} else { + die <<EOM if $opt->{update}; +--update requires `-d EXTDIR' +EOM + for my $gd (@git_dirs) { + my $cd = "$gd/public-inbox-cindex"; + my $cidx = PublicInbox::CodeSearchIdx->new($cd, { %$opt }); + $cidx->{-cidx_internal} = 1; + @{$cidx->{git_dirs}} = ($gd); + $cidx->cidx_run; + } +} diff --git a/script/public-inbox-clone b/script/public-inbox-clone index 54059d03..c3e64485 100755 --- a/script/public-inbox-clone +++ b/script/public-inbox-clone @@ -2,14 +2,14 @@ # Copyright (C) all contributors <meta@public-inbox.org> # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt> # Wrapper to git clone remote public-inboxes -use strict; -use v5.10.1; +use v5.12; use Getopt::Long qw(:config gnu_getopt no_ignore_case auto_abbrev); my $opt = {}; my $help = <<EOF; # the following should fit w/o scrolling in 80x24 term: -usage: public-inbox-clone INBOX_URL [DESTINATION] +usage: public-inbox-clone [OPTIONS] INBOX_URL [INBOX_DIR] + public-inbox-clone [OPTIONS] ROOT_URL [DESTINATION] - clone remote public-inboxes + clone remote public-inboxes or grokmirror manifests options: @@ -17,12 +17,23 @@ options: --torsocks VAL whether or not to wrap git and curl commands with torsocks (default: `auto') Must be one of: `auto', `no' or `yes' + --dry-run | -n show what would be cloned without cloning --verbose | -v increase verbosity (may be repeated) - --quiet | -q increase verbosity (may be repeated) + --quiet | -q disable progress reporting -C DIR chdir to specified directory + +See public-inbox-clone(1) man page for --manifest, --remote-manifest, +--objstore, --project-list, --post-update-hook, --include, --exclude, +--prune, --keep-going, --jobs, --inbox-config EOF -GetOptions($opt, qw(help|h quiet|q verbose|v+ C=s@ c=s@ - no-torsocks torsocks=s epoch=s)) or die $help; + +# cgit calls it `project-list', grokmirror calls it `projectslist', +# support both :/ +GetOptions($opt, qw(help|h quiet|q verbose|v+ C=s@ c=s@ include|I=s@ exclude=s@ + inbox-config=s inbox-version=i objstore=s manifest=s + remote-manifest=s project-list|projectslist=s post-update-hook=s@ + prune|p keep-going|k exit-code purge + dry-run|n jobs|j=i no-torsocks torsocks=s epoch=s)) or die $help; if ($opt->{help}) { print $help; exit }; require PublicInbox::Admin; # loads Config PublicInbox::Admin::do_chdir(delete $opt->{C}); @@ -35,12 +46,10 @@ defined($dst) or ($dst) = ($url =~ m!/([^/]+)/?\z!); index($dst, "\n") >= 0 and die "`\\n' not allowed in `$dst'"; # n.b. this is still a truckload of code... -require URI; +require File::Spec; require PublicInbox::LEI; require PublicInbox::LeiExternal; require PublicInbox::LeiMirror; -require PublicInbox::LeiCurl; -require PublicInbox::Lock; $url = PublicInbox::LeiExternal::ext_canonicalize($url); my $lei = bless { @@ -52,8 +61,10 @@ open $lei->{3}, '.' or die "open . $!"; my $mrr = bless { lei => $lei, src => $url, - dst => $dst, + dst => File::Spec->canonpath($dst), }, 'PublicInbox::LeiMirror'; + +$? = 0; $mrr->do_mirror; -$mrr->can('_wq_done_wait')->([$mrr, $lei], $$); +$mrr->can('_wq_done_wait')->($$, $mrr, $lei); exit(($lei->{child_error} // 0) >> 8); diff --git a/script/public-inbox-compact b/script/public-inbox-compact index 80d0224b..1062be5a 100755 --- a/script/public-inbox-compact +++ b/script/public-inbox-compact @@ -1,12 +1,12 @@ #!perl -w -# Copyright (C) 2018-2021 all contributors <meta@public-inbox.org> +# Copyright (C) all contributors <meta@public-inbox.org> # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt> -use strict; -use v5.10.1; +use v5.12; use Getopt::Long qw(:config gnu_getopt no_ignore_case auto_abbrev); -my $opt = { compact => 1, -coarse_lock => 1, -eidx_ok => 1 }; +my $opt = { compact => 1, -coarse_lock => 1, + -eidx_ok => 1, -cidx_ok => 1 }; my $help = <<EOF; # the following should fit w/o scrolling in 80x24 term: -usage: public-inbox-compact <INBOX_DIR|EXTINDEX_DIR> +usage: public-inbox-compact <INBOX_DIR|EXTINDEX_DIR|CINDEX_DIR> Compact Xapian DBs in an inbox @@ -31,12 +31,14 @@ PublicInbox::Admin::progress_prepare($opt); require PublicInbox::InboxWritable; require PublicInbox::Xapcmd; my $cfg = PublicInbox::Config->new; -my ($ibxs, $eidxs) = PublicInbox::Admin::resolve_inboxes(\@ARGV, $opt, $cfg); -unless ($ibxs) { print STDERR $help; exit 1 } +my ($ibxs, $eidxs, $cidxs) = + PublicInbox::Admin::resolve_inboxes(\@ARGV, $opt, $cfg); +unless (@$ibxs || @$eidxs || @$cidxs) { print STDERR $help; exit 1 } for my $ibx (@$ibxs) { $ibx = PublicInbox::InboxWritable->new($ibx); PublicInbox::Xapcmd::run($ibx, 'compact', $opt); } -for my $eidx (@$eidxs) { - PublicInbox::Xapcmd::run($eidx, 'compact', $opt); +for my $ibxish (@$eidxs, @$cidxs) { + my $restore = $ibxish->can('prep_umask') ? $ibxish->prep_umask : undef; + PublicInbox::Xapcmd::run($ibxish, 'compact', $opt); } diff --git a/script/public-inbox-convert b/script/public-inbox-convert index 42955a48..713c2881 100755 --- a/script/public-inbox-convert +++ b/script/public-inbox-convert @@ -1,5 +1,5 @@ #!/usr/bin/perl -w -# Copyright (C) 2018-2021 all contributors <meta@public-inbox.org> +# Copyright (C) all contributors <meta@public-inbox.org> # License: AGPL-3.0+ <http://www.gnu.org/licenses/agpl-3.0.txt> use strict; use v5.10.1; @@ -63,7 +63,7 @@ if (delete $old->{-unconfigured}) { } die "Only conversion from v1 inboxes is supported\n" if $old->version >= 2; -my $detected = PublicInbox::Admin::detect_indexlevel($old); +my $detected = $old->detect_indexlevel; $old->{indexlevel} //= $detected; my $env; if ($opt->{'index'}) { @@ -75,7 +75,7 @@ if ($opt->{'index'}) { } local %ENV = (%$env, %ENV) if $env; my $new = { %$old }; -$new->{inboxdir} = $cfg->rel2abs_collapsed($new_dir); +$new->{inboxdir} = PublicInbox::Config::rel2abs_collapsed($new_dir); $new->{version} = 2; $new = PublicInbox::InboxWritable->new($new, { nproc => $opt->{jobs} }); $new->{-no_fsync} = 1 if !$opt->{fsync}; @@ -89,7 +89,8 @@ sub link_or_copy ($$) { File::Copy::cp($src, $dst) or die "cp $src, $dst failed: $!\n"; } -$old->with_umask(sub { +{ + my $restore = $old->with_umask; my $old_cfg = "$old->{inboxdir}/config"; local $ENV{GIT_CONFIG} = $old_cfg; my $new_cfg = "$new->{inboxdir}/all.git/config"; @@ -110,18 +111,16 @@ $old->with_umask(sub { my $desc = "$old->{inboxdir}/description"; link_or_copy($desc, "$new->{inboxdir}/description") if -e $desc; my $clone = "$old->{inboxdir}/cloneurl"; - if (-e $clone) { - warn <<""; + warn <<"" if -e $clone; $clone may not be valid after migrating to v2, not copying - } -}); +} my $state = ''; my $head = $old->{ref_head} || 'HEAD'; -my ($rd, $pid) = $old->git->popen(qw(fast-export --use-done-feature), $head); +my $rd = $old->git->popen(qw(fast-export --use-done-feature), $head); $v2w->idx_init($opt); my $im = $v2w->importer; -my ($r, $w) = $im->gfi_start; +my $io = $im->gfi_start; my $h = '[0-9a-f]'; my %D; my $last; @@ -131,23 +130,17 @@ while (<$rd>) { } elsif (/^commit /) { $state = 'commit'; } elsif (/^data ([0-9]+)/) { - my $len = $1; - print $w $_ or $im->wfail; - while ($len) { - my $n = read($rd, my $tmp, $len) or die "read: $!"; - warn "$n != $len\n" if $n != $len; - $len -= $n; - print $w $tmp or $im->wfail; - } + print $io $_ or $im->wfail; + print $io PublicInbox::IO::read_all($rd, $1) or $im->wfail; next; } elsif ($state eq 'commit') { if (m{^M 100644 :([0-9]+) (${h}{2}/${h}{38})}o) { my ($mark, $path) = ($1, $2); $D{$path} = $mark; if ($last && $last ne 'm') { - print $w "D $last\n" or $im->wfail; + print $io "D $last\n" or $im->wfail; } - print $w "M 100644 :$mark m\n" or $im->wfail; + print $io "M 100644 :$mark m\n" or $im->wfail; $last = 'm'; next; } @@ -155,20 +148,18 @@ while (<$rd>) { my $mark = delete $D{$1}; defined $mark or die "undeleted path: $1\n"; if ($last && $last ne 'd') { - print $w "D $last\n" or $im->wfail; + print $io "D $last\n" or $im->wfail; } - print $w "M 100644 :$mark d\n" or $im->wfail; + print $io "M 100644 :$mark d\n" or $im->wfail; $last = 'd'; next; } } last if $_ eq "done\n"; - print $w $_ or $im->wfail; + print $io $_ or $im->wfail; } -close $rd or die "close fast-export: $!\n"; -waitpid($pid, 0) or die "waitpid failed: $!\n"; -$? == 0 or die "fast-export failed: $?\n"; -$r = $w = undef; # v2w->done does the actual close and error checking +$rd->close or die "fast-export: \$?=$? \$!=$!\n"; +$io = undef; $v2w->done; if (my $old_mm = $old->mm) { $old->cleanup; diff --git a/script/public-inbox-edit b/script/public-inbox-edit index 1fbaf5a7..88115d7c 100755 --- a/script/public-inbox-edit +++ b/script/public-inbox-edit @@ -1,5 +1,5 @@ #!/usr/bin/perl -w -# Copyright (C) 2019-2021 all contributors <meta@public-inbox.org> +# Copyright (C) all contributors <meta@public-inbox.org> # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt> # # Used for editing messages in a public-inbox. @@ -184,12 +184,10 @@ retry_edit: # rename/relink $edit_fn open my $new_fh, '<', $edit_fn or die "can't read edited file ($edit_fn): $!\n"; - defined(my $new_raw = do { local $/; <$new_fh> }) or die - "read $edit_fn: $!\n"; + my $new_raw = PublicInbox::IO::read_all $new_fh; if (!$opt->{raw}) { - # get rid of the From we added - $new_raw =~ s/\A[\r\n]*From [^\r\n]*\r?\n//s; + PublicInbox::Eml::strip_from($new_raw); # check if user forgot to purge (in mutt) after editing if ($new_raw =~ /^From /sm) { diff --git a/script/public-inbox-extindex b/script/public-inbox-extindex index bee824b1..2e5a5d2c 100755 --- a/script/public-inbox-extindex +++ b/script/public-inbox-extindex @@ -32,7 +32,7 @@ GetOptions($opt, qw(verbose|v+ reindex rethread compact|c+ jobs|j=i indexlevel|index-level|L=s max_size|max-size=s batch_size|batch-size=s dedupe:s@ gc commit-interval=i watch scan! dry-run|n - all C=s@ help|h)) + multi-pack-index! all C=s@ help|h)) or die $help; if ($opt->{help}) { print $help; exit 0 }; die "--jobs must be >= 0\n" if defined $opt->{jobs} && $opt->{jobs} < 0; diff --git a/script/public-inbox-fetch b/script/public-inbox-fetch index f9bac4e3..6fd15328 100755 --- a/script/public-inbox-fetch +++ b/script/public-inbox-fetch @@ -2,8 +2,7 @@ # Copyright (C) all contributors <meta@public-inbox.org> # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt> # Wrapper to git fetch remote public-inboxes -use strict; -use v5.10.1; +use v5.12; use Getopt::Long qw(:config gnu_getopt no_ignore_case auto_abbrev); my $opt = {}; my $help = <<EOF; # the following should fit w/o scrolling in 80x24 term: @@ -24,6 +23,7 @@ options: -C DIR chdir to specified directory EOF GetOptions($opt, qw(help|h quiet|q verbose|v+ C=s@ c=s@ try-remote|T=s@ + prune|p no-torsocks torsocks=s exit-code)) or die $help; if ($opt->{help}) { print $help; exit }; require PublicInbox::Fetch; # loads Admin diff --git a/script/public-inbox-index b/script/public-inbox-index index a04be9fc..a13e44bf 100755 --- a/script/public-inbox-index +++ b/script/public-inbox-index @@ -44,6 +44,7 @@ GetOptions($opt, qw(verbose|v+ reindex rethread compact|c+ jobs|j=i prune batch_size|batch-size=s since|after=s until|before=s sequential-shard|seq-shard + multi-pack-index! no-update-extindex update-extindex|E=s@ fast-noop|F skip-docdata all C=s@ help|h)) or die $help; @@ -66,6 +67,7 @@ $opt->{-use_cwd} = 1; my @ibxs = PublicInbox::Admin::resolve_inboxes(\@ARGV, $opt, $cfg); PublicInbox::Admin::require_or_die('-index'); unless (@ibxs) { print STDERR $help; exit 1 } +require PublicInbox::InboxWritable; my (@eidx, %eidx_seen); my $update_extindex = $opt->{'update-extindex'}; @@ -96,8 +98,9 @@ for my $ei_name (@$update_extindex) { my $mods = {}; my @eidx_unconfigured; foreach my $ibx (@ibxs) { + $ibx = PublicInbox::InboxWritable->new($ibx); # detect_indexlevel may also set $ibx->{-skip_docdata} - my $detected = PublicInbox::Admin::detect_indexlevel($ibx); + my $detected = $ibx->detect_indexlevel; # XXX: users can shoot themselves in the foot, with opt->{indexlevel} $ibx->{indexlevel} //= $opt->{indexlevel} // ($opt->{xapian_only} ? 'full' : $detected); @@ -111,17 +114,14 @@ The following inboxes are unconfigured and will not be updated in @$update_extindex:\n@eidx_unconfigured EOF -# "Search::Xapian" includes SWIG "Xapian", too: -$opt->{compact} = 0 if !$mods->{'Search::Xapian'}; +$opt->{compact} = 0 if !$mods->{'Xapian'}; # (or old Search::Xapian) PublicInbox::Admin::require_or_die(keys %$mods); my $env = PublicInbox::Admin::index_prepare($opt, $cfg); local %ENV = (%ENV, %$env) if $env; -require PublicInbox::InboxWritable; PublicInbox::Xapcmd::check_compact() if $opt->{compact}; PublicInbox::Admin::progress_prepare($opt); for my $ibx (@ibxs) { - $ibx = PublicInbox::InboxWritable->new($ibx); if ($opt->{compact} >= 2) { PublicInbox::Xapcmd::run($ibx, 'compact', $opt->{compact_opt}); } diff --git a/script/public-inbox-init b/script/public-inbox-init index 5de45781..cf6443f7 100755 --- a/script/public-inbox-init +++ b/script/public-inbox-init @@ -1,9 +1,10 @@ #!perl -w -# Copyright (C) 2014-2021 all contributors <meta@public-inbox.org> +# Copyright (C) all contributors <meta@public-inbox.org> # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt> use strict; use v5.10.1; use Getopt::Long qw/:config gnu_getopt no_ignore_case auto_abbrev/; +use autodie qw(open chmod close rename); use Fcntl qw(:DEFAULT); my $help = <<EOF; # the following should fit w/o scrolling in 80x24 term: usage: public-inbox-init NAME INBOX_DIR HTTP_URL ADDRESS [ADDRESS..] @@ -60,7 +61,7 @@ my $inboxdir = shift @ARGV or $usage_cb->(); my $http_url = shift @ARGV or $usage_cb->(); my (@address) = @ARGV; @address or $usage_cb->(); -+PublicInbox::Admin::do_chdir(\@chdir); +PublicInbox::Admin::do_chdir(\@chdir); @c_extra = map { my ($k, $v) = split(/=/, $_, 2); @@ -121,18 +122,17 @@ sysopen($lockfh, $lockfile, O_RDWR|O_CREAT|O_EXCL) or do { exit(255); }; require PublicInbox::OnDestroy; -my $auto_unlink = PublicInbox::OnDestroy->new($$, sub { unlink $lockfile }); -my ($perm, %seen); +my $auto_unlink = PublicInbox::OnDestroy::on_destroy(sub { unlink $lockfile }); +my $perm = 0644 & ~umask; +my %seen; if (-e $pi_config) { - open(my $oh, '<', $pi_config) or die "unable to read $pi_config: $!\n"; - my @st = stat($oh); + require PublicInbox::IO; + open(my $oh, '<', $pi_config); + my @st = stat($oh) or die "(f)stat failed on $pi_config: $!\n"; $perm = $st[2]; - defined $perm or die "(f)stat failed on $pi_config: $!\n"; - chmod($perm & 07777, $fh) or - die "(f)chmod failed on future $pi_config: $!\n"; - defined(my $old = do { local $/; <$oh> }) or die "read $pi_config: $!\n"; - print $fh $old or die "failed to write: $!\n"; - close $oh or die "failed to close $pi_config: $!\n"; + chmod($perm & 07777, $fh); + print $fh PublicInbox::IO::read_all($oh); + close $oh; # yes, this conflict checking is racy if multiple instances of this # script are run by the same $PI_DIR @@ -159,7 +159,7 @@ if (-e $pi_config) { $indexlevel //= $ibx->{indexlevel} if $ibx; } my $pi_config_tmp = $fh->filename; -close($fh) or die "failed to close $pi_config_tmp: $!\n"; +close($fh); my $pfx = "publicinbox.$name"; my @x = (qw/git config/, "--file=$pi_config_tmp"); @@ -214,12 +214,12 @@ $ibx->init_inbox(0, $skip_epoch, $skip_artnum); my $f = "$inboxdir/description"; if (sysopen $fh, $f, O_CREAT|O_EXCL|O_WRONLY) { - print $fh "public inbox for $address[0]\n" or die "print($f): $!"; - close $fh or die "close($f): $!"; + print $fh "public inbox for $address[0]\n"; + close $fh; } # needed for git prior to v2.1.0 -umask(0077) if defined $perm; +umask(0077); require PublicInbox::Spawn; PublicInbox::Spawn->import(qw(run_die)); @@ -246,11 +246,6 @@ for my $kv (@c_extra) { } # needed for git prior to v2.1.0 -if (defined $perm) { - chmod($perm & 07777, $pi_config_tmp) or - die "(f)chmod failed on future $pi_config: $!\n"; -} - -rename $pi_config_tmp, $pi_config or - die "failed to rename `$pi_config_tmp' to `$pi_config': $!\n"; +chmod($perm & 07777, $pi_config_tmp); +rename $pi_config_tmp, $pi_config; undef $auto_unlink; # trigger ->DESTROY diff --git a/script/public-inbox-learn b/script/public-inbox-learn index 8b8e1b77..a955cdf6 100755 --- a/script/public-inbox-learn +++ b/script/public-inbox-learn @@ -28,6 +28,7 @@ use PublicInbox::Spamcheck::Spamc; use Getopt::Long qw(:config gnu_getopt no_ignore_case auto_abbrev); my %opt = (all => 0); GetOptions(\%opt, qw(all help|h)) or die $help; +use PublicInbox::Import; my $train = shift or die $help; if ($train !~ /\A(?:ham|spam|rm)\z/) { @@ -37,10 +38,12 @@ die "--all only works with `rm'\n" if $opt{all} && $train ne 'rm'; my $spamc = PublicInbox::Spamcheck::Spamc->new; my $pi_cfg = PublicInbox::Config->new; +local $PublicInbox::Import::DROP_UNIQUE_UNSUB; +PublicInbox::Import::load_config($pi_cfg); my $err; my $mime = PublicInbox::Eml->new(do{ - defined(my $data = do { local $/; <STDIN> }) or die "read STDIN: $!\n"; - $data =~ s/\A[\r\n]*From [^\r\n]*\r?\n//s; + my $data = PublicInbox::IO::read_all \*STDIN; + PublicInbox::Eml::strip_from($data); if ($train ne 'rm') { eval { @@ -64,6 +67,7 @@ sub remove_or_add ($$$$) { $ibx->{name} = $ENV{GIT_COMMITTER_NAME} // $ibx->{name}; $ibx->{-primary_address} = $ENV{GIT_COMMITTER_EMAIL} // $addr; $ibx = PublicInbox::InboxWritable->new($ibx); + $ibx->{indexlevel} = $ibx->detect_indexlevel; my $im = $ibx->importer(0); if ($train eq "rm") { @@ -109,12 +113,12 @@ if ($train eq 'spam' || ($train eq 'rm' && $opt{all})) { my %seen; while (my ($addr, $ibx) = each %dests) { next unless ref($ibx); # $ibx may be 0 - next if $seen{"$ibx"}++; + next if $seen{0 + $ibx}++; remove_or_add($ibx, $train, $mime, $addr); } my $dests = PublicInbox::MDA->inboxes_for_list_id($pi_cfg, $mime); for my $ibx (@$dests) { - next if $seen{"$ibx"}++; + next if $seen{0 + $ibx}++; remove_or_add($ibx, $train, $mime, $ibx->{-primary_address}); } } diff --git a/script/public-inbox-mda b/script/public-inbox-mda index 7e2bee92..b463b07b 100755 --- a/script/public-inbox-mda +++ b/script/public-inbox-mda @@ -1,5 +1,5 @@ #!/usr/bin/perl -w -# Copyright (C) 2013-2021 all contributors <meta@public-inbox.org> +# Copyright (C) all contributors <meta@public-inbox.org> # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt> # # Mail delivery agent for public-inbox, run from your MTA upon mail delivery @@ -16,8 +16,14 @@ use strict; use Getopt::Long qw(:config gnu_getopt no_ignore_case auto_abbrev); my ($ems, $emm, $show_help); my $precheck = 1; +use PublicInbox::Import; +local $PublicInbox::Import::DROP_UNIQUE_UNSUB; # does this need a CLI switch? GetOptions('precheck!' => \$precheck, 'help|h' => \$show_help) or do { print STDERR $help; exit 1 }; +if ($show_help) { + print $help; + exit; +} my $do_exit = sub { my ($code) = shift; @@ -33,13 +39,13 @@ use PublicInbox::Filter::Base; use PublicInbox::InboxWritable; use PublicInbox::Spamcheck; -# n.b: hopefully we can setup the emergency path without bailing due to -# user error, we really want to setup the emergency destination ASAP +# n.b.: Hopefully we can set up the emergency path without bailing due to +# user error, we really want to set up the emergency destination ASAP # in case there's bugs in our code or user error. my $emergency = $ENV{PI_EMERGENCY} || "$ENV{HOME}/.public-inbox/emergency/"; $ems = PublicInbox::Emergency->new($emergency); -my $str = do { local $/; <STDIN> }; -$str =~ s/\A[\r\n]*From [^\r\n]*\r?\n//s; +my $str = PublicInbox::IO::read_all \*STDIN; +PublicInbox::Eml::strip_from($str); $ems->prepare(\$str); my $eml = PublicInbox::Eml->new(\$str); my $cfg = PublicInbox::Config->new; @@ -47,6 +53,8 @@ my $key = 'publicinboxmda.spamcheck'; my $default = 'PublicInbox::Spamcheck::Spamc'; my $spamc = PublicInbox::Spamcheck::get($cfg, $key, $default); my $dests = []; +PublicInbox::Import::load_config($cfg, $do_exit); + my $recipient = $ENV{ORIGINAL_RECIPIENT}; if (defined $recipient) { my $ibx = $cfg->lookup($recipient); # first check @@ -55,7 +63,8 @@ if (defined $recipient) { if (!scalar(@$dests)) { $dests = PublicInbox::MDA->inboxes_for_list_id($cfg, $eml); if (!scalar(@$dests) && !defined($recipient)) { - die "ORIGINAL_RECIPIENT not defined in ENV\n"; + warn "ORIGINAL_RECIPIENT not defined in ENV\n"; + $do_exit->(67); # EX_NOUSER } scalar(@$dests) or $do_exit->(67); # EX_NOUSER 5.1.1 user unknown } diff --git a/script/public-inbox-purge b/script/public-inbox-purge index 121027cc..618cfec4 100755 --- a/script/public-inbox-purge +++ b/script/public-inbox-purge @@ -1,5 +1,5 @@ #!/usr/bin/perl -w -# Copyright (C) 2019-2021 all contributors <meta@public-inbox.org> +# Copyright (C) all contributors <meta@public-inbox.org> # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt> # # Used for purging messages entirely from a public-inbox. Currently @@ -33,8 +33,8 @@ PublicInbox::Admin::do_chdir(delete $opt->{C}); my @ibxs = PublicInbox::Admin::resolve_inboxes(\@ARGV, $opt); PublicInbox::AdminEdit::check_editable(\@ibxs); -defined(my $data = do { local $/; <STDIN> }) or die "read STDIN: $!\n"; -$data =~ s/\A[\r\n]*From [^\r\n]*\r?\n//s; +my $data = PublicInbox::IO::read_all \*STDIN; +PublicInbox::Eml::strip_from($data); my $n_purged = 0; foreach my $ibx (@ibxs) { diff --git a/script/public-inbox-watch b/script/public-inbox-watch index af02d8f3..9bcd42ed 100755 --- a/script/public-inbox-watch +++ b/script/public-inbox-watch @@ -1,5 +1,5 @@ #!/usr/bin/perl -w -# Copyright (C) 2016-2021 all contributors <meta@public-inbox.org> +# Copyright (C) all contributors <meta@public-inbox.org> # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt> my $help = <<EOF; usage: public-inbox-watch @@ -11,13 +11,15 @@ use strict; use Getopt::Long qw(:config gnu_getopt no_ignore_case auto_abbrev); use IO::Handle; # ->autoflush use PublicInbox::Watch; +use PublicInbox::Import; +local $PublicInbox::Import::DROP_UNIQUE_UNSUB; use PublicInbox::Config; use PublicInbox::DS; my $do_scan = 1; GetOptions('scan!' => \$do_scan, # undocumented, testing only 'help|h' => \(my $show_help)) or do { print STDERR $help; exit 1 }; if ($show_help) { print $help; exit 0 }; -my $oldset = PublicInbox::DS::block_signals(); +PublicInbox::DS::block_signals(); STDOUT->autoflush(1); STDERR->autoflush(1); local $0 = $0; # local since this script may be eval-ed @@ -27,7 +29,8 @@ my $reload = sub { $watch->quit; $watch = PublicInbox::Watch->new(PublicInbox::Config->new); if ($watch) { - warn("I: reloaded\n"); + $watch->{sig} = $prev->{sig}; # prevent redundant signalfd + warn "# reloaded\n"; } else { warn("E: reloading failed\n"); $watch = $prev; @@ -37,10 +40,10 @@ my $reload = sub { if ($watch) { my $scan = sub { return if !$watch; - warn "I: scanning\n"; + warn "# scanning\n"; $watch->trigger_scan('full'); }; - my $quit = sub { + my $quit = sub { # may be called in IMAP/NNTP children $watch->quit if $watch; $watch = undef; $0 .= ' quitting'; @@ -51,8 +54,9 @@ if ($watch) { CHLD => \&PublicInbox::DS::enqueue_reap, }; $sig->{QUIT} = $sig->{TERM} = $sig->{INT} = $quit; + local @SIG{keys %$sig} = values(%$sig); # for non-signalfd/kqueue # --no-scan is only intended for testing atm, undocumented. PublicInbox::DS::requeue($scan) if $do_scan; - $watch->watch($sig, $oldset) while ($watch); + $watch->watch($sig) while ($watch); } diff --git a/script/public-inbox-xcpdb b/script/public-inbox-xcpdb index 24fc5a25..fac54559 100755 --- a/script/public-inbox-xcpdb +++ b/script/public-inbox-xcpdb @@ -1,11 +1,10 @@ #!perl -w -# Copyright (C) 2019-2021 all contributors <meta@public-inbox.org> +# Copyright (C) all contributors <meta@public-inbox.org> # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt> -use strict; -use v5.10.1; +use v5.12; use Getopt::Long qw(:config gnu_getopt no_ignore_case auto_abbrev); my $help = <<EOF; # the following should fit w/o scrolling in 80x24 term: -usage: public-inbox-xcpdb [options] <INBOX_DIR|EXTINDEX_DIR> +usage: public-inbox-xcpdb [options] <INBOX_DIR|EXTINDEX_DIR|CINDEX_DIR> upgrade or reshard Xapian DB(s) used by public-inbox @@ -26,7 +25,8 @@ index options (see public-inbox-index(1) man page for full description): See public-inbox-xcpdb(1) man page for full documentation. EOF -my $opt = { quiet => -1, compact => 0, fsync => 1, -eidx_ok => 1 }; +my $opt = { quiet => -1, compact => 0, fsync => 1, + -eidx_ok => 1, -cidx_ok => 1 }; GetOptions($opt, qw( fsync|sync! compact|c reshard|R=i max_size|max-size=s batch_size|batch-size=s @@ -42,8 +42,9 @@ PublicInbox::Admin::do_chdir(delete $opt->{C}); require PublicInbox::Config; my $cfg = PublicInbox::Config->new; -my ($ibxs, $eidxs) = PublicInbox::Admin::resolve_inboxes(\@ARGV, $opt, $cfg); -unless ($ibxs) { print STDERR $help; exit 1 } +my ($ibxs, $eidxs, $cidxs) = + PublicInbox::Admin::resolve_inboxes(\@ARGV, $opt, $cfg); +unless (@$ibxs || @$eidxs || @$cidxs) { print STDERR $help; exit 1 } my $idx_env = PublicInbox::Admin::index_prepare($opt, $cfg); # we only set XAPIAN_FLUSH_THRESHOLD for index, since cpdb doesn't @@ -63,6 +64,7 @@ for my $ibx (@$ibxs) { PublicInbox::Xapcmd::run($ibx, 'cpdb', $opt); } -for my $eidx (@$eidxs) { - PublicInbox::Xapcmd::run($eidx, 'cpdb', $opt); +for my $ibxish (@$eidxs, @$cidxs) { + my $restore = $ibxish->can('prep_umask') ? $ibxish->prep_umask : undef; + PublicInbox::Xapcmd::run($ibxish, 'cpdb', $opt); } |