Added newline for debug output
Moving subcommands from {command} to {subcommand} key

Beginning with this change, we can easily tell which command is the shell command (stored in {command}) and which command is for controlling the shell command (stored in {subcommand}).
Killing all background jobs at start

Easier during the test phase when the program is restarted frequently without rebooting the virtual machine every time.
Added {requestrc} and refactoring code

If
   requestrc => 1
is specified for a hash element in the @vm_steps array, the curl command is automatically added.

To get this work, {command} mustn't end with <ENTER>.

Default for requestrc is 0 (by default, no return code should be sent to the virtual machine).
Provide step number with curl call

Changed URL to fit both step number and return code.
Track vm's state

Compiles successfully but no further tests done.

Besides that, I don't know if there may be raise conditions and how to avoid those, if any.
Named $1, $2 after regex
Wait until step completes successfully

Unfortunately, this doesn't work at the moment.

It seems the loop doesn't recognize the values set by the thread.

Output excerpt:
Use of uninitialized value in printf at ./webserver-thread-test.pl line 435.
Use of uninitialized value in printf at ./webserver-thread-test.pl line 435.
Last step completed: 0, return code: 0.
Use of uninitialized value in numeric eq (==) at ./webserver-thread-test.pl line 437.
Die if creation of web server daemon fails

Hopefully avoids error message:
Thread 1 terminated abnormally: Can't call method "sockhost" on an undefined value at ./webserver-thread-test.pl line 271.
Sharing between threads

Simplified handling for storing return code and number of last completed step.

The hash %vm_state didn't work:
Thread 2 terminated abnormally: Invalid value for shared scalar at ./webserver-thread-test.pl line 222.

For now, only the two above mentioned values are stored so the program runs fine at the moment.