I run a while(1) loop in perl to pull out email addresses and each one's configuration values from PostgreSQL tables. Right now, I write a temporary file and use neomutt -nF the_temp_file with system. Then I unlink the file. Neomutt quits. Then the loop gives me the list of email addresses to start neomutt again with any one of those addresses I select.
I haven't asked this question yet on the neomutt mailing list, but I will. I would like to know in general if there is a way to imitate the temporary file without writing one into the file system.
To be more clear:
Get the config values, like:
set beep = 0
set beep_new = 0
set bounce = ask-yes
set check_mbox_size = 1
set check_new = 1
and send that directly to the spot neomutt expects a file at neomutt -F config_file
Is this possible? Thanks
CodePudding user response:
It still involves a temporary file, but if you're using an OS like Linux that has a /dev/fd
filesystem, you can open a temporary file, immediately delete it to keep things tidy, and pass /dev/fd/N
as the filename to neomutt
, where N is the underlying file descriptor number of the perl file handle. If you use the core File::Temp
module to create the temporary file, it can be done securely without potential race conditions or having to manually delete the file.
There is a bit of drudgery in stopping the descriptor from being closed before system
executes the child program, though.
Example:
#!/usr/bin/env perl
use strict;
use warnings;
use File::Temp qw/tempfile/;
use Fcntl qw/:DEFAULT/;
# Get a handle to an anonymous temporary file
my $fh = tempfile;
print $fh <<EOF;
set beep = 0
set beep_new = 0
set bounce = ask-yes
set check_mbox_size = 1
set check_new = 1
EOF
flush $fh;
# Clear the CLOEXEC bit so the descriptor is available to the program run
# by system
my $flags = fcntl $fh, F_GETFD, 0
or die "Unable to get descriptor flags: $!";
fcntl $fh, F_SETFD, $flags & ~FD_CLOEXEC
or die "Unable to set descriptor flags: $!";
my $fd = fileno $fh;
system("cat", "/dev/fd/$fd");
An alternative that completely avoids temporary files (but is a bit more complicated) is to open up a pipe, and fork off a child that writes the data to it, and again using the /dev/fd/N
interface with neomutt:
#!/usr/bin/env perl
use strict;
use warnings;
use Fcntl qw/:DEFAULT/;
pipe my $reader, my $writer or die "Unable to pipe: $!\n";
my $pid = fork;
die "Unable to fork" unless defined $pid;
if ($pid == 0) { # Child process
close $reader;
print $writer <<EOF;
set beep = 0
set beep_new = 0
set bounce = ask-yes
set check_mbox_size = 1
set check_new = 1
EOF
close $writer;
exit;
} else { # Parent process
close $writer;
# Clear the CLOEXEC bit so the descriptor is available to the program run
# by system
my $flags = fcntl $reader, F_GETFD, 0;
fcntl $reader, F_SETFD, $flags & ~FD_CLOEXEC;
my $fd = fileno $reader;
system("cat", "/dev/fd/$fd");
close $reader;
waitpid $pid, 0; # Reap the child process
}