- my @cmd = ($self->ykchalresp, "-n$device", "-$slot", qw{-H -i-}, $timeout == 0 ? '-N' : ());
- my ($pid, $child_in, $child_out, $child_err) = _run_ykpers(@cmd);
- push @cleanup, Scope::Guard->new(sub { kill $pid if defined $pid });
-
- # Set up an alarm [mostly] safely
- my $prev_alarm = 0;
- local $SIG{ALRM} = sub {
- $prev_alarm -= $timeout;
- throw 'Timed out while waiting for challenge response',
- command => \@cmd,
- challenge => $challenge,
- timeout => $timeout,
- };
- $prev_alarm = alarm $timeout if 0 < $timeout;
- push @cleanup, Scope::Guard->new(sub { alarm($prev_alarm < 1 ? 1 : $prev_alarm) }) if $prev_alarm;
-
- local $SIG{PIPE} = 'IGNORE';
- binmode($child_in);
- print $child_in pad_pkcs7($challenge, 64);
- close($child_in);
-
- binmode($child_out);
- binmode($child_err);
- my $resp = do { local $/; <$child_out> };
- my $err = do { local $/; <$child_err> };
- chomp($resp, $err);
-
- waitpid($pid, 0);
- undef $pid;
- my $exit_status = $? >> 8;
- alarm 0;
-
- my $yk_errno = _yk_errno($err);
- $exit_status == 0 or throw 'Failed to receive challenge response: ' . ($err ? $err : ''),
- error => $err,
- yk_errno => $yk_errno || 0;
-
- $resp =~ /^[A-Fa-f0-9]+$/ or throw 'Unexpected response from challenge', response => $resp;
+ my @cmd = ($self->_program('ykchalresp'), "-n$device", "-$slot", qw{-H -i-}, $timeout == 0 ? '-N' : ());
+
+ my $r;
+ my $try = 0;
+ TRY:
+ {
+ $r = $self->_run_ykpers(\@cmd, {
+ (0 < $timeout ? (timeout => $timeout) : ()),
+ child_stdin => pad_pkcs7($challenge, 64),
+ terminate_on_parent_sudden_death => 1,
+ });
+
+ if (my $t = $r->{timeout}) {
+ throw 'Timed out while waiting for challenge response',
+ command => \@cmd,
+ challenge => $challenge,
+ timeout => $t,
+ result => $r;
+ }
+
+ my $exit_code = $r->{exit_code};
+ if ($exit_code != 0) {
+ my $err = $r->{stderr};
+ chomp $err;
+ my $yk_errno = _yk_errno($err);
+ if ($yk_errno == YK_EUSBERR && $err =~ /resource busy/i && ++$try <= $RETRY_COUNT) {
+ sleep $RETRY_INTERVAL;
+ goto TRY;
+ }
+ throw 'Failed to receive challenge response: ' . ($err ? $err : 'Something happened'),
+ error => $err,
+ yk_errno => $yk_errno || 0;
+ }
+ }
+
+ my $resp = $r->{stdout};
+ chomp $resp;
+ $resp =~ /^[A-Fa-f0-9]+$/ or throw 'Unexpected response from challenge', response => $resp, result => $r;