Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
#!/usr/bin/env ruby
# workaround-upstart-snafu
#
# When lied to about the behavior of a job’s main process wrt. forking with the
# “expect” stanza, Upstart can get into a confused state where it’s tracking a
# nonexistent pid.
#
# This hack creates new short-lived processes until one gets the pid in
# question, then has its parent die so that it’s going to get reaped by pid 1
# (Upstart), which will get Upstart out of the confused state.
#
# When “status <jobname>” says “<jobname> stop/killed, process 12345” and
# there’s no such process, run “workaround-upstart-snafu 12345” and wait until
# it exits.
# Copyright © 2012 Johan Kiviniemi <devel@johan.kiviniemi.name>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
class Workaround
def initialize target_pid
@target_pid = target_pid
quit_if_process_exists
first_child
end
def quit_if_process_exists
warning = "There is already a process running with the PID #{@target_pid}, try killing it?"
Process.kill(0, @target_pid)
$stderr.puts(warning)
exit 1
rescue Errno::EPERM
$stderr.puts(warning)
exit 1
rescue Errno::ESRCH
puts "No process with that PID currently running - attempting to obtain the PID."
end
def first_child
pid = fork do
Process.setsid
rio, wio = IO.pipe
# Keep rio open
until second_child rio, wio
print "\e[A"
end
end
Process.wait pid
end
def second_child parent_rio, parent_wio
rio, wio = IO.pipe
pid = fork do
rio.close
parent_wio.close
puts "%20.20s" % Process.pid
if Process.pid == @target_pid
wio << 'a'
wio.close
parent_rio.read
end
end
wio.close
begin
if rio.read == 'a'
true
else
Process.wait pid
false
end
ensure
rio.close
end
end
end
if $0 == __FILE__
pid = ARGV.shift
raise "USAGE: #{$0} pid" if pid.nil?
Workaround.new Integer pid
end
@roadsideseb

This comment has been minimized.

Copy link
Owner Author

@roadsideseb roadsideseb commented Dec 22, 2015

This script is mentioned in a bug report for upstart which describes the failed start of an upstart process breaking subsequent start or stop command because the PID for the initially failed process is in a broken state. Here's the original bug report.

The solution is using this script to fake the broken PID, kill it and therefore make upstart recognize the process has been killed:

$ wget https://gist.github.com/elbaschid/817cb1070caaf5d2efd8/raw/9ea6679f8d63b075ee8f1854b2f863682dbf1dc8/workaround-upstart-snafu
$ chmod +x workaround-upstart-snafu
$ ./workaround-upstart-snafu PID

To get the broken PID you can run:

$ initctl status <upstart name>
<upstart name> start/running, process 31618
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment