Joseph Riopelle
Joseph Riopelle

Reputation: 179

Expect premature EOF when spawning forking process

I have a rather long/complicated TCL/Expect script that runs an openssl ocsp responder in both CGI and Daemon modes. The script is complete and functional but I want to implement a multi-threaded OCSP daemon using openssl ocsp -multi <#> ... and it is showing some strange behavior.

Here is the most succinct example code I could come up with (saved as ocsp.tcl):

#!/usr/bin/tclsh
#usage (run as root):
#./ocsp.tcl [multi] [sudo] [internal] [debug] [trace] [cmdtrace] [null] [raw] [rawinit] [forever]
package require Expect;                                     #make sure expect is loaded

if {[lsearch $::argv trace]!=-1} { strace 5 };              #arg - walk through expect script
if {[lsearch $::argv debug]!=-1} { exp_debug 1 };           #arg - prompt through expect script
if {[lsearch $::argv internal]!=-1} { exp_internal 1 };     #arg - show expect match logic
if {[lsearch $::argv null]!=-1} { remove_nulls 0 };         #arg - keep nulls in expect
if {[lsearch $::argv rawinit]!=-1} { set stty_init raw };   #arg - spawn with raw terminal
if {[lsearch $::argv forever]!=-1} { set timeout -1 };      #arg - expect doesn't time out

if {[lsearch $::argv sudo]!=-1} { lappend cmd sudo -n };    #arg - sudo before command
if {[lsearch $::argv cmdtrace]!=-1} { lappend cmd strace }; #arg - trace openssl execution
lappend cmd openssl ocsp -index index.txt -CA ca.crt;       #create command line
lappend cmd -rsigner ocsp.crt -rkey ocsp.key -nmin 300;     #create command line
lappend cmd -text -ignore_err -port 2650 -timeout 5;        #create command line
if {[lsearch $::argv multi]!=-1} { lappend cmd -multi 5 };  #arg - launch 5 child responders

puts "Command: $::argv0 $::argv";                           #debug - launch arguments
puts -nonewline "TCL: ${::tcl_patchLevel}\t";               #debug - TCL 8.6.10
puts -nonewline "Expect: [exp_version]\t";                  #debug - Expect 5.45.4
puts "OpenSSL: [exec -- openssl version]";                  #debug - OpenSSL 1.1.1g 21 Apr 2020
puts "OS: [exec -- lsb_release -ds] [exec -- uname -rm]";   #debug - Ubuntu 20.04.1 LTS 5.4.0-42-lowlatency x86_64
puts -nonewline "remove_nulls: [remove_nulls]\t";           #debug - remove nulls
puts -nonewline "match_max: [match_max]\t";                 #debug - match_max
puts "parity: [parity]\tdebug: [exp_debug]";                #debug - parity, exp_debug
lappend ps ps -o pid=,ppid=,tt=,cmd= -C openssl;            #ps output syntax
#-----------------------------------------------------------
spawn {*}$cmd;                                              #run and show openssl command
#-----------------------------------------------------------
if {[lsearch $::argv raw]!=-1} { stty raw };                #arg - raw output to expect
puts -nonewline "spawn_id: ${spawn_id}\t";                  #debug - spawn_id
puts -nonewline "user_spawn_id: ${::user_spawn_id}\t";      #debug - user_spawn_id
puts -nonewline "error_spawn_id: ${::error_spawn_id}\t";    #debug - error_spawn_id
puts "tty_spawn_id: ${::tty_spawn_id}";                     #debug - tty_spawn_id
catch {puts "-----Spawn-----\n[exec -- {*}$ps]"};           #debug - openssl processes

expect {
  -re "Enter pass phrase for .*:" {
    catch {puts "\n-----Pre-Send-----\n[exec -- {*}$ps]"};  #debug - openssl processes
    exp_send "<password>\n";                              #input the key password
    catch {puts "-----Post-Send-----\n[exec -- {*}$ps]"};   #debug - openssl processes
    exp_continue;                                           #restart this expect loop
  }
  timeout { puts "Expect: Timeout"; exp_continue };         #debug - timeout
  full_buffer { puts "Expect: Full Buffer"; exp_continue }; #debug - full buffer
  #null { puts "Expect: Null"; exp_continue };              #debug - null (??SEGFAULTS??)
  eof { puts "Expect: We Should Never Get Here" };          #!!PROGRAM SHOULD NEVER REACH EOF!!
}

lassign [wait] pid spawnid os err;                          #get spawn exit details
puts -nonewline "pid: ${pid}\tspawn_id: ${spawnid}\t";      #show exit status
puts "os_error: ${os}\terror_num: ${err}";                  #show exit status

This script can be run with any options in any order like so: ./ocsp.tcl [multi] [sudo] [internal] [debug] [trace] [cmdtrace] [null] [raw] [rawinit] [forever]

If everything goes correctly the script shouldn't terminate. Instead it should just end up repeating:

Expect: Timeout
Expect: Timeout
Expect: Timeout

Obviously some of the options will warp this output (debug, internal, trace, cmdtrace), but with the same end result.

To summarize:

When the script fails, tail -f /var/log/syslog outputs ocsp[<pid>]: fatal: error detaching from parent process group: Operation not permitted. A look at apps/lib/http_server.c in the OpenSSL source shows that this is message is caused by a failed setpgid. setpgid describes Operation not permitted as:

EPERM - An attempt was made to move a process into a process group in a different session, or to change the process group ID of one of the children of the calling process and the child was in a different session, or to change the process group ID of a session leader (setpgid(), setpgrp()).

Where I am at:

Right now I could just admit defeat and spawn sudo ... but it is hackish.

I'm currently trying to supplement/replace spawn with:

set pty_pid [spawn -noecho -pty]; #just create a pty? or do we make one for std[in|out|err]?
stty raw -echo < $spawn_out(slave,name); #don't echo stdin on the new pty?
#stty raw > $spawn_out(slave,name); #raw stdout on the new pty? THIS KILLS CTRL-C
set spawn_pid [exec -- {*}$cmd > $spawn_out(slave,name) < $spawn_out(slave,name) &] #redirect stin/out to the pty?
close -slave; #no idea

...but as you can tell by the snippet I'm not sure what specifically to do with these pty(s).

Any help getting -multi working would be greatly appreciated.

Edits:

Upvotes: 1

Views: 325

Answers (1)

strafer
strafer

Reputation: 1

I got around this problem when running in a container, using the initialization system to correctly handle process forks, I used tini, but most likely another one will do.

Perhaps the behavior of openssl-ocsp has changed from the version in which the author of the question experimented earlier, but without this trick I got the same error.

A fragmentary example of a Containerfile/Dockerfile with the versions used:

FROM docker.io/library/alpine

…

apk add --no-cache \
    tini==0.19.0-r3   \
    openssl==3.3.2-r1 \

…

ENTRYPOINT ["/sbin/tini", "--"]

CMD ["openssl", "ocsp", "-multi", …]

Upvotes: 0

Related Questions