1

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

ChrisB
  • 13
  • 3

1 Answers1

1

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
}
Shawn
  • 47,241
  • 3
  • 26
  • 60
  • Come to think of it, you might not need the fork in the second one if your data is small enough to fit in the kernel pipe buffer all at once. – Shawn Oct 06 '21 at 01:25
  • Thanks. The second one without temporary files works fine. I like avoiding temporary files for password security, which is the one thing that had me worried. Spam is kinda bad to send out if someone can snag your password and username. I appreciate your help. – ChrisB Oct 18 '21 at 20:28
  • Actually, at first I thought what I came up with was OK. However, I am using $dbh with PostgreSQL. Apparently, $dbh handles do not also fork, but need to get a new connection within each child. I think that I need to ask a new question with better details about exactly what I am doing because of the $dbh problem. It's probably best if I write a smaller program that just hits on the exact problems without all of the subroutines that deal with options and sorting stuff. If anyone has any good links for my to look at, I would appreciate that. The PostgreSQL problem adds on to the mysteries here – ChrisB Oct 30 '21 at 00:51
  • I'm going to look at DBIx::Connector. It might be the answer – ChrisB Oct 30 '21 at 01:42