From e5631087d3862823d0d4854a8dfc1258f91cb115 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Thu, 12 Sep 2019 08:34:20 +0000 Subject: tmpfile: give temporary files meaningful names Although we always unlink temporary files, give them a meaningful name so that we can we can still make sense of the pre-unlink name when using lsof(8) or similar tools on Linux. --- MANIFEST | 1 + lib/PublicInbox/Git.pm | 4 +++- lib/PublicInbox/GitHTTPBackend.pm | 4 +++- lib/PublicInbox/HTTP.pm | 10 ++++++---- lib/PublicInbox/SolverGit.pm | 3 ++- lib/PublicInbox/Tmpfile.pm | 38 ++++++++++++++++++++++++++++++++++++++ lib/PublicInbox/ViewVCS.pm | 3 ++- 7 files changed, 55 insertions(+), 8 deletions(-) create mode 100644 lib/PublicInbox/Tmpfile.pm diff --git a/MANIFEST b/MANIFEST index 24280351..777367d0 100644 --- a/MANIFEST +++ b/MANIFEST @@ -135,6 +135,7 @@ lib/PublicInbox/Spawn.pm lib/PublicInbox/SpawnPP.pm lib/PublicInbox/Syscall.pm lib/PublicInbox/TLS.pm +lib/PublicInbox/Tmpfile.pm lib/PublicInbox/Unsubscribe.pm lib/PublicInbox/UserContent.pm lib/PublicInbox/V2Writable.pm diff --git a/lib/PublicInbox/Git.pm b/lib/PublicInbox/Git.pm index d048051e..ff3838b3 100644 --- a/lib/PublicInbox/Git.pm +++ b/lib/PublicInbox/Git.pm @@ -12,6 +12,7 @@ use warnings; use POSIX qw(dup2); require IO::Handle; use PublicInbox::Spawn qw(spawn popen_rd); +use PublicInbox::Tmpfile; use base qw(Exporter); our @EXPORT_OK = qw(git_unquote git_quote); @@ -110,7 +111,8 @@ sub _bidi_pipe { qw(-c core.abbrev=40 cat-file), $batch); my $redir = { 0 => fileno($out_r), 1 => fileno($in_w) }; if ($err) { - open(my $fh, '+>', undef) or fail($self, "open.err failed: $!"); + my $id = "git.$self->{git_dir}$batch.err"; + my $fh = tmpfile($id) or fail($self, "tmpfile($id): $!"); $self->{$err} = $fh; $redir->{2} = fileno($fh); } diff --git a/lib/PublicInbox/GitHTTPBackend.pm b/lib/PublicInbox/GitHTTPBackend.pm index c8878145..a8337035 100644 --- a/lib/PublicInbox/GitHTTPBackend.pm +++ b/lib/PublicInbox/GitHTTPBackend.pm @@ -12,6 +12,7 @@ use HTTP::Date qw(time2str); use HTTP::Status qw(status_message); use Plack::Util; use PublicInbox::Qspawn; +use PublicInbox::Tmpfile; # 32 is same as the git-daemon connection limit my $default_limiter = PublicInbox::Qspawn::Limiter->new(32); @@ -218,7 +219,8 @@ sub input_prepare { if (defined $fd && $fd >= 0) { return { 0 => $fd }; } - open(my $in, '+>', undef); + my $id = "git-http.input.$env->{REMOTE_HOST}:$env->{REMOTE_PORT}"; + my $in = tmpfile($id); unless (defined $in) { err($env, "could not open temporary file: $!"); return; diff --git a/lib/PublicInbox/HTTP.pm b/lib/PublicInbox/HTTP.pm index 19b57d59..b43ef870 100644 --- a/lib/PublicInbox/HTTP.pm +++ b/lib/PublicInbox/HTTP.pm @@ -21,6 +21,7 @@ use IO::Handle; require PublicInbox::EvCleanup; use PublicInbox::DS qw(msg_more); use PublicInbox::Syscall qw(EPOLLIN EPOLLONESHOT); +use PublicInbox::Tmpfile; use constant { CHUNK_START => -1, # [a-f0-9]+\r\n CHUNK_END => -2, # \r\n @@ -325,8 +326,9 @@ sub response_write { } sub input_tmpfile ($) { - open($_[0], '+>', undef); - $_[0]->autoflush(1); + my $input = tmpfile('http.input', $_[0]->{sock}) or return; + $input->autoflush(1); + $input; } sub input_prepare { @@ -338,10 +340,10 @@ sub input_prepare { quit($self, 413); return; } - input_tmpfile($input); + $input = input_tmpfile($self); } elsif (env_chunked($env)) { $len = CHUNK_START; - input_tmpfile($input); + $input = input_tmpfile($self); } else { $input = $null_io; } diff --git a/lib/PublicInbox/SolverGit.pm b/lib/PublicInbox/SolverGit.pm index 49f94895..8878961e 100644 --- a/lib/PublicInbox/SolverGit.pm +++ b/lib/PublicInbox/SolverGit.pm @@ -15,6 +15,7 @@ use Fcntl qw(SEEK_SET); use PublicInbox::Git qw(git_unquote git_quote); use PublicInbox::MsgIter qw(msg_iter msg_part_text); use PublicInbox::Qspawn; +use PublicInbox::Tmpfile; use URI::Escape qw(uri_escape_utf8); # POSIX requires _POSIX_ARG_MAX >= 4096, and xargs is required to @@ -235,7 +236,7 @@ sub prepare_index ($) { my $path_a = $di->{path_a} or die "BUG: path_a missing for $oid_full"; my $mode_a = $di->{mode_a} || extract_old_mode($di); - open my $in, '+>', undef or die "open: $!"; + my $in = tmpfile("update-index.$oid_full") or die "tmpfile: $!"; print $in "$mode_a $oid_full\t$path_a\0" or die "print: $!"; $in->flush or die "flush: $!"; sysseek($in, 0, 0) or die "seek: $!"; diff --git a/lib/PublicInbox/Tmpfile.pm b/lib/PublicInbox/Tmpfile.pm new file mode 100644 index 00000000..7fda100e --- /dev/null +++ b/lib/PublicInbox/Tmpfile.pm @@ -0,0 +1,38 @@ +# Copyright (C) 2019 all contributors +# License: AGPL-3.0+ +package PublicInbox::Tmpfile; +use strict; +use warnings; +use base qw(Exporter); +our @EXPORT = qw(tmpfile); +use Fcntl qw(:DEFAULT); +use Errno qw(EEXIST); +require File::Spec; + +# use tmpfile instead of open(..., '+>', undef) so we can get an +# unlinked filename which makes sense when viewed with lsof +# (at least on Linux) +# TODO: O_APPEND support (this is the reason I'm not using File::Temp) +# And if we ever stop caring to have debuggable filenames, O_TMPFILE :) +sub tmpfile ($;$) { + my ($id, $sock) = @_; + if (defined $sock) { + # add the socket inode number so we can figure out which + # socket it belongs to + my @st = stat($sock); + $id .= '-ino:'.$st[1]; + } + $id =~ tr!/!^!; + + my $fl = O_RDWR | O_CREAT | O_EXCL; + do { + my $fn = File::Spec->tmpdir . "/$id-".time.'-'.rand; + if (sysopen(my $fh, $fn, $fl, 0600)) { # likely + unlink($fn) or die "unlink($fn): $!"; # FS broken + return $fh; # success + } + } while ($! == EEXIST); + undef # EMFILE/ENFILE/ENOSPC/ENOMEM +} + +1; diff --git a/lib/PublicInbox/ViewVCS.pm b/lib/PublicInbox/ViewVCS.pm index 60a62e57..369afe93 100644 --- a/lib/PublicInbox/ViewVCS.pm +++ b/lib/PublicInbox/ViewVCS.pm @@ -20,6 +20,7 @@ use bytes (); # only for bytes::length use PublicInbox::SolverGit; use PublicInbox::WwwStream; use PublicInbox::Linkify; +use PublicInbox::Tmpfile; use PublicInbox::Hval qw(ascii_html to_filename); my $hl = eval { require PublicInbox::HlMod; @@ -185,7 +186,7 @@ sub show ($$;$) { $hints->{$to} = $v; } - open my $log, '+>', undef or die "open: $!"; + my $log = tmpfile("solve.$oid_b"); my $solver = PublicInbox::SolverGit->new($ctx->{-inbox}, sub { solve_result($ctx, $_[0], $log, $hints, $fn); }); -- cgit v1.2.3-24-ge0c7