In an older version of our code, we called out from Perl to do an LDAP search as follows:
# Pass the base DN in via the ldapsearch-specific environment variable
# (rather than as the "-b" paramater) to avoid problems of shell
# interpretation of special characters in the DN.
$ENV{LDAP_BASEDN} = $ldn;
$lcmd = "ldapsearch -x -T -1 -h $gLdapServer" .
<snip>
" > $lworkfile 2>&1";
system($lcmd);
if (($? != 0) || (! -e "$lworkfile"))
{
# Handle the error
}
The code above would result in a successful LDAP search, and the output of that search would be in the file $lworkfile
.
Unfortunately, we recently reconfigured openldap on this server so that a "BASE DC=" is specified in /etc/openldap/ldap.conf and /etc/ldap.conf. That change seems to mean ldapsearch ignores the LDAP_BASEDN environment variable, and so my ldapsearch fails.
I've tried a couple of different fixes but without success so far:
(1) I tried going back to using the "-b" argument to ldapsearch, but escaping the shell metacharacters. I started writing the escaping code:
my $ldn_escaped = $ldn;
$ldn_escaped =~ s/\/\\/g;
$ldn_escaped =~ s/`/\`/g;
$ldn_escaped =~ s/$/\$/g;
$ldn_escaped =~ s/"/\"/g;
That threw up some Perl errors because I haven't escaped those regexes properly in Perl (the line number matches the regex with the backticks in).
Backticks found where operator expected at /tmp/mycommand line 404, at end of line
At the same time I started to doubt this approach and looked for a better one.
(2) I then saw some Stackoverflow questions (here and here) that suggested a better solution.
Here's the code:
print("Processing...");
# Pass the arguments to ldapsearch by invoking open() with an array.
# This ensures the shell does NOT interpret shell metacharacters.
my(@cmd_args) = ("-x", "-T", "-1", "-h", "$gLdapPool",
"-b", "$ldn",
<snip>
);
$lcmd = "ldapsearch";
open my $lldap_output, "-|", $lcmd, @cmd_args;
while (my $lline = <$lldap_output>)
{
# I can parse the contents of my file fine
}
$lldap_output->close;
The two problems I am having with approach (2) are:
a) Calling open or system with an array of arguments does not let me pass > $lworkfile 2>&1
to the command, so I can't stop the ldapsearch output being sent to screen, which makes my output look ugly:
Processing...ldap_bind: Success (0) additional info: Success
b) I can't figure out how to choose which location (i.e. path and file name) to the file handle passed to open
, i.e. I don't know where $lldap_output
is. Can I move/rename it, or inspect it to find out where it is (or is it not actually saved to disk)?
Based on the problems with (2), this makes me think I should return back to approach (1), but I'm not quite sure how to
I would use IPC::Run3 for this. This is much like the
open '-|'
approach, but allows you to redirect STDERR too.Note:
$lldap_output
is a pipe reading fromldapsearch
. There's no file being created on disk.If you want a file on disk, you could use IPC::Run3 like this:
This is like approach (1), but using
-b
instead of$ENV{LDAP_BASEDN}
.Thanks to Greg Hewgill for the answer. I'm posting my code below in case it helps anybody else wanting to use the open3 function.
Here's a hacky way to read both STDOUT and STDERR from an external program with multiple arguments using plain ol' "open":
Now just read $program_output to get both STDOUT and STDERR.
One approach would be to use
IPC::Open3
to enable your Perl code to handle both the stdout and stderr streams of your external program.See the docs for open. You can duplicate and redirect STDERR, run your command, then restore STDERR. It's more verbose than using any of the IPC::(Open3, Run, Run3, etc.) libraries, but possible to do without them if you can't/won't install extra modules, or don't want to use IPC::Open3.