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

1

There are 1 answers

4
Shawn On BEST ANSWER

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
}