Running a script with cyradm throwing ReadLine errors
Binarus
lists at binarus.de
Wed Dec 19 08:48:53 EST 2018
Dear ellie,
On 19.12.2018 01:38, ellie timoney wrote:
> I did a bit of reading, and apparently Term::ReadLine is a stub module that just loads "an implementation", which in your case wants to be Term::ReadLine::Gnu. My guess is that, when you uninstall Term::ReadLine::Gnu, Term::ReadLine no longer successfully compiles because it's missing an implementation, and consequently the fallback code I pointed out previously is used instead. So, from this I'm concluding that the "correct setup" from above is adequate for the Cyrus::IMAP::DummyReadline interface, but is not sufficient for a real ReadLine implementation. Sounds like we've found our bug!
Some additional findings:
1) Cyrus::IMAP::DummyReadLine
-----------------------------
Looking again at that code
# ugh. ugh. suck. aieee.
my $use_rl = 'Cyrus::IMAP::DummyReadline';
{
if (eval { require Term::ReadLine; }) {
$use_rl = 'Term::ReadLine';
}
}
I believe that $use_rl *always* equals 'Term::ReadLine' after having
executed it. This is for the following reason: In newer Perl versions,
Term::ReadLine is a core module. Everybody has it installed. This means
that the require Term::ReadLine will always be successful.
I did a test to prove that. I uninstalled Term::ReadLine::Gnu again and
changed the code above to the following (note the last line):
# ugh. ugh. suck. aieee.
my $use_rl = 'Cyrus::IMAP::DummyReadline';
{
if (eval { require Term::ReadLine; }) {
$use_rl = 'Term::ReadLine';
}
}
print $use_rl."\n";
As expected, perl -MCyrus::IMAP::Shell -e 'run("./000")' now prints
Term::ReadLine
as first line on the terminal. This was still the case (as expected
again) after reinstalling Term::ReadLine::Gnu.
*That means:*
Cyrus::IMAP::DummyReadLine is not related to the problem or its solution
in any way. It never gets pulled in, at least with recent Perl
distributions which have Term::ReadLine included [as a core module].
2) *__DATA__ variable / file handle
-----------------------------------
After having read the Perl docs about that mysterious __DATA__ variable
(see below), grep'ing the whole Perl module trees for the string
__DATA__, and analyzing the results, I came to the conclusion that the
*__DATA__ variable *never* is assigned any value during normal program
execution, meaning that _run() always is called with undef as its last
parameter.
As a proof, I have replaced the following code
# trivial; wrapper for _run with correct setup
sub run {
my $cyradm;
_run(\$cyradm, [*STDIN, *STDOUT, *STDERR], *__DATA__);
}
by
# trivial; wrapper for _run with correct setup
sub run {
my $cyradm;
print Dumper(${*Cyrus::IMAP::Shell::__DATA__})."\n";
_run(\$cyradm, [*STDIN, *STDOUT, *STDERR], *__DATA__);
}
and have added use Data::Dumper at the beginning of the file.
Now, when executing perl -MCyrus::IMAP::Shell -e 'run("./000")', it printed
$VAR1 = undef;
as the first line on the terminal. This was the case whether
Term::ReadLine::Gnu was installed or not.
To further back that finding, I reverted my changes and then changed the
code again as follows (note the last parameter to _run()):
# trivial; wrapper for _run with correct setup
sub run {
my $cyradm;
_run(\$cyradm, [*STDIN, *STDOUT, *STDERR], undef);
}
This did not change the module's behavior compared to the original code.
While it now threw the errors described in my first post again (as
expected) when Term::ReadLine::Gnu was installed, it threw no errors
when it was not installed.
*That means:*
*__DATA__ (the third parameter to _run) is always undef, and this does
not lead to errors being thrown or the compilation / execution being
aborted as long as Term::ReadLine::Gnu is not installed, but makes
Term::ReadLine::Gnu (if it is installed) throw errors and abort the
compilation / execution of the script.
(Too) short explanation of the __DATA__ variable:
This is a predefined filehandle in Perl which could be used as follows.
Suppose you have a script:
package ...
[code here]
__DATA__
data value 1
data value 2
...
Then you can access the data values (i.e. all values which come behind
the __DATA__ statement) using the special filehandle [PACKAGE
NAME]::DATA (or __DATA__ as well?) from within the package code.
For details, see https://perldoc.perl.org/perldata.html#Special-Literals
Since there is no __DATA__ statement in any of Cyrus' Perl modules or in
modules they use, it is clear that the *__DATA__ filehandle is always
undef. To be honest, I can't understand why it is used. I originally
thought that it would be initialized by some other module (directly or
indirectly) which is used by Cyrus::IMAP::Shell, but my analysis showed
that it isn't (unless I have missed something, which might well be the
case).
3) No script execution at all
-----------------------------
I have to apologize that I didn't mention this in my first post; the
reason was that I did my first tests with an *empty* script.
However, now that my script is meaningful, I noticed that it did not get
executed at all even if Term::ReadLine::Gnu was not installed. In other
words, when I uninstalled Term::ReadLine::Gnu again and ran
perl -MCyrus::IMAP::Shell -e 'run("./000")'
the script "000" was *not* executed when the original version of
Cyrus::IMAP::Shell was in place. I didn't notice this from the beginning
on because I did my first tests with an empty script, and no errors were
thrown.
When I changed the module's code and assigned *__DATA__ a handle to the
file desired (as shown in my previous post), the script was executed.
To put it all together:
-----------------------
- Cyrus::IMAP::DummyReadLine is never used if the Perl distribution is
recent (i.e. already includes Term::ReadLine). This applies whether
Term::ReadLine::Gnu is installed or not.
- _run() is always called with undef as third parameter. If
Term::ReadLine::Gnu is installed, errors are thrown if we execute perl
-MCyrus::IMAP::Shell -e 'run("./000")' in a terminal. No errors are
thrown if Term::ReadLine::Gnu is not installed. The errors
Term::ReadLine::Gnu throws are clearly due to the fact that the third
argument to _run() is undef (the error thrown reads "Bad filehandle:
__DATA__ at ...").
- With the original version of Cyrus::IMAP::Shell, even when
Term::ReadLine::Gnu is not installed, the script <name> is not executed
when we issue perl -MCyrus::IMAP::Shell -e 'run("<name>")' in a terminal.
- If we assign *__DATA__ the filehandle to the desired script in the
run() function before calling _run(), the desired script does get
executed when we issue perl -MCyrus::IMAP::Shell -e 'run("./000")'. This
works whether Term::ReadLine::Gnu is installed or not.
Hence, the problem is clearly related to Cyrus::IMAP::Shell. It does
either just not execute the script given (if Term::ReadLine::Gnu is not
installed), or it makes Term::ReadLine::Gnu throw errors (if it is
installed) due to the uninitialized file handle.
It would be very nice if somebody could fix that bug or correct
Cyrus::IMAP::Shell's man page accordingly.
Fortunately, there is a workaround. We could make cyradm execute a
script directly with no problem:
cat 000 | cyradm
Shame on me that I didn't see this earlier - it would have solved my
problem and would have saved me a whole day or two. I guess
Cyrus::IMAP::Shell's man page had distracted me too much...
However, I'd still be strongly interested in a bug fix for the module.
In fact, I already have a clean solution in mind, but I'd like to test
it before posting it here. I'll report back in a few hours.
Thank you very much again!
Regards,
Binarus
More information about the Info-cyrus
mailing list