为什么 perl qx 挂在 Mojolicious::Lite 而不是普通程序?
Why does perl qx hang in Mojolicious::Lite but not in an ordinary program?
(这是为 x86_64-linux 构建的 cperl 5,版本 24,颠覆 4 (v5.24.4c))
Ubuntu 18.04.
下面是一个有效的程序。但是,当我从内部 运行 这个程序
Mojolicious::Lite(版本 6.04),它挂了。
使用 top,我看到“tr”是吃掉所有 CPU 的那个。
我试过使用 cat 而不是 tr 但它仍然挂起。
如果我 Control-C Mojo 代码,它会打印密码然后退出。
这就像 tr 正在接受 urandom 字节但没有移动到折叠
直到我打断它。
但是,这适用于普通脚本,不适用于 Mojo...
有人知道为什么吗?
热烈
约翰
有效的脚本:
#! /usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
my $pass_length = 3;
my $exec = qq{tr -cd "[:alnum:]" < /dev/urandom | fold -w$pass_length | head -n1};
print Dumper $exec;
my $pass = qx{$exec};
chomp $pass;
print Dumper $pass;
挂起的Mojolicious Lite代码:
use Mojolicious::Lite;
use strict;
use warnings;
use Data::Dumper;
post 'testit' => sub {
my $c = shift;
my $pass_length = 3;
# tr -cd '[:alnum:]' < /dev/urandom | fold -w30 | head -n1
my $exec = qq{tr -cd '[:alnum:]' < /dev/urandom | fold -w$pass_length | head -n1};
warn Dumper $exec;
my $pass = qx{$exec};
chomp $pass;
warn Dumper $pass;
return $c->render( json => { foo => 'bar'} );
};
app->secrets('foobar');
app->start;
我不确定为什么 tr
在直接从 /dev/urandom
获取输入时挂起。我能够通过在 tr
之前有一个中间 cat
管道来解决它。以下对我有用:
my $exec = qq{head -1 /dev/urandom | cat | tr -cd '[:alnum:'] | fold -w$pass_length}
编辑:
如果你想生成$pass_length
个字符的密码,你可以这样使用head -c
:
my $exec = qq{head -1 /dev/urandom | cat | tr -cd '[:alnum:'] | head -c $pass_length}
sub gen_password {
my ($pass_len) = @_;
# We use sysread to avoid wasting entropy by over-reading.
# We use :raw because we use sysread.
state $bad_syms = {
map { $_ => 1 }
qw( 0 O I 1 l )
};
state $ok_syms = {
map { $_ => 1 }
grep !$bad_syms->{$_},
'a'..'z', 'A'..'Z', '0'..'9'
};
my $qfn = '/dev/urandom';
open(my $fh, '<:raw', $qfn)
or die("Can't open $qfn: $!\n");
my $password = '';
while (length($password) < $pass_len) {
my $rv = sysread($fh, my $ch, 1);
die("Can't read $qfn: $!\n") if !defined($rv);
die("Can't read $qfn: Premature EOF\n") if !$rv;
redo if !$ok_syms->{$ch};
$password .= $ch;
}
return $password;
}
好处:
- 效率更高。
- 更少的故障模式。
- 更好的错误处理。
- 保证是要求的长度。
- 避免容易混淆的相似字符。
以下版本浪费的熵更少,但需要一组恰好 64 个符号:
use MIME::Base64 qw( encode_base64 );
sub gen_password {
my ($pass_len) = @_;
my $qfn = '/dev/urandom';
open(my $fh, '<:raw', $qfn)
or die("Can't open $qfn: $!\n");
my $bytes = int( ($pass_len+3) * (3/4) );
my $buf = '';
while ($bytes) {
my $rv = sysread($fh, $buf, $bytes, length($buf));
die("Can't read $qfn: $!\n") if !defined($rv);
die("Can't read $qfn: Premature EOF\n") if !$rv;
$bytes -= $rv;
}
return substr(
encode_base64($buf, '') =~
tr/a-zA-Z0-9+\//a-km-zA-HJ-NP-Z2-9!%^&*()/r,
0, $pass_len,
);
}
(这是为 x86_64-linux 构建的 cperl 5,版本 24,颠覆 4 (v5.24.4c)) Ubuntu 18.04.
下面是一个有效的程序。但是,当我从内部 运行 这个程序 Mojolicious::Lite(版本 6.04),它挂了。 使用 top,我看到“tr”是吃掉所有 CPU 的那个。 我试过使用 cat 而不是 tr 但它仍然挂起。 如果我 Control-C Mojo 代码,它会打印密码然后退出。 这就像 tr 正在接受 urandom 字节但没有移动到折叠 直到我打断它。 但是,这适用于普通脚本,不适用于 Mojo...
有人知道为什么吗?
热烈
约翰
有效的脚本:
#! /usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
my $pass_length = 3;
my $exec = qq{tr -cd "[:alnum:]" < /dev/urandom | fold -w$pass_length | head -n1};
print Dumper $exec;
my $pass = qx{$exec};
chomp $pass;
print Dumper $pass;
挂起的Mojolicious Lite代码:
use Mojolicious::Lite;
use strict;
use warnings;
use Data::Dumper;
post 'testit' => sub {
my $c = shift;
my $pass_length = 3;
# tr -cd '[:alnum:]' < /dev/urandom | fold -w30 | head -n1
my $exec = qq{tr -cd '[:alnum:]' < /dev/urandom | fold -w$pass_length | head -n1};
warn Dumper $exec;
my $pass = qx{$exec};
chomp $pass;
warn Dumper $pass;
return $c->render( json => { foo => 'bar'} );
};
app->secrets('foobar');
app->start;
我不确定为什么 tr
在直接从 /dev/urandom
获取输入时挂起。我能够通过在 tr
之前有一个中间 cat
管道来解决它。以下对我有用:
my $exec = qq{head -1 /dev/urandom | cat | tr -cd '[:alnum:'] | fold -w$pass_length}
编辑:
如果你想生成$pass_length
个字符的密码,你可以这样使用head -c
:
my $exec = qq{head -1 /dev/urandom | cat | tr -cd '[:alnum:'] | head -c $pass_length}
sub gen_password {
my ($pass_len) = @_;
# We use sysread to avoid wasting entropy by over-reading.
# We use :raw because we use sysread.
state $bad_syms = {
map { $_ => 1 }
qw( 0 O I 1 l )
};
state $ok_syms = {
map { $_ => 1 }
grep !$bad_syms->{$_},
'a'..'z', 'A'..'Z', '0'..'9'
};
my $qfn = '/dev/urandom';
open(my $fh, '<:raw', $qfn)
or die("Can't open $qfn: $!\n");
my $password = '';
while (length($password) < $pass_len) {
my $rv = sysread($fh, my $ch, 1);
die("Can't read $qfn: $!\n") if !defined($rv);
die("Can't read $qfn: Premature EOF\n") if !$rv;
redo if !$ok_syms->{$ch};
$password .= $ch;
}
return $password;
}
好处:
- 效率更高。
- 更少的故障模式。
- 更好的错误处理。
- 保证是要求的长度。
- 避免容易混淆的相似字符。
以下版本浪费的熵更少,但需要一组恰好 64 个符号:
use MIME::Base64 qw( encode_base64 );
sub gen_password {
my ($pass_len) = @_;
my $qfn = '/dev/urandom';
open(my $fh, '<:raw', $qfn)
or die("Can't open $qfn: $!\n");
my $bytes = int( ($pass_len+3) * (3/4) );
my $buf = '';
while ($bytes) {
my $rv = sysread($fh, $buf, $bytes, length($buf));
die("Can't read $qfn: $!\n") if !defined($rv);
die("Can't read $qfn: Premature EOF\n") if !$rv;
$bytes -= $rv;
}
return substr(
encode_base64($buf, '') =~
tr/a-zA-Z0-9+\//a-km-zA-HJ-NP-Z2-9!%^&*()/r,
0, $pass_len,
);
}