Skip to content

Instantly share code, notes, and snippets.

@anddam
Last active September 8, 2015 06:01
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anddam/3feeeafd4b951341c026 to your computer and use it in GitHub Desktop.
Save anddam/3feeeafd4b951341c026 to your computer and use it in GitHub Desktop.
A neater F-Script injection

A neater F-Script injection service

Last updated 2014-12-16

Rationale - what is this

This is an injection service for F-Script provided as Automator workflow.

The services I found around the web never completely worked well for me, usually I got the Automator workflow icon in menubar running forever. Also I noticed that they left a lldb process running even after the injected program had quit.

I checked and noticed that lldb doesn't cope well with the absence of stdin and that is exactly how it is usually executed when called from a shell script in an Automator workflow. Moreover I didn't quite like the approach with a tempfile that was being used.

I checked lldb's doc and the proper way to run a non-interactive session is via its scripting API, that happens to be in Python.

Since Python is such a joyful language I wrote my own little injection script the way it was meant to be.

Update 2014-12-16

Due to an issue with lldb on 10.10 the process running the python interpreter gets stuck, I therefore switched to using a shell script and a temporary file.

I'm updating the following instruction to reflect that, the workflow script is tested and working fine.

How to install the injection service

In order to create the service follow these steps:

  • open Automator
  • create a new document of type "Service"
  • drag a Run Applescript element to the workflow
  • copy the applescript script from this gist into the newly created element
  • drag a Run Shell Script element to the workflow
  • copy the shell script from this gist into the newly created element
  • set the shell to /bin/sh, set "Pass input" setting to "as arguments"
  • save the service as "Inject F-Script" or any other name you prefer into ~/Library/services

How to use the injection service

  • Open an application
  • go to the application menu > Services
  • run the injection service

The lldb issue with stdin

If you are curious to see the kind of issue lldb gets when called without a proper stdin just run:

lldb </dev/null

This will produce a lot of garbage and either correctly exit with status 0 or get stuck.

When passing commands from a script file with --source flag the outcome is a bit different, resulting in either a stuck process or a memory fault, try:

echo "quit" >/tmp/lldb.tmp; lldb -s /tmp/lldb.tmp </dev/null

These lines are unharmful, to clean the stuck process just kill them. If you didn't have previous running lldb instance you can simply run killall lldb

tell application "System Events" to get (the unix id of the first process whose frontmost is true) as text
import sys
# get the PID or exit the script
try:
pid = str(sys.argv[1])
except (IndexError, ValueError):
sys.exit("Error: couldn't get a valid PID")
# pull in the lldb Python API, edit the base directory path if needed
# the following value is taken from the output of 'lldb -P' on my system
lldb_module_path = '/Applications/Xcode.app/Contents/SharedFrameworks'
sys.path.append(lldb_module_path + '/LLDB.framework/Resources/Python')
try:
import lldb
except ImportError:
sys.exit("Error: couldn't load lldb module. Check that LLDB.framework is in " + lldb_module_path)
# execute a command, print the output if it succeeds, the error otherwise
def run_command(interpreter, command, result):
interpreter.HandleCommand(command, result)
if result.Succeeded():
status = 'Success'
output = result.GetOutput()
else:
status = 'Error'
output = result.GetError()
#print('%s executing """%s""", output:\n%s' % (status, command, output))
# init the lldb objects needed to run commands
result = lldb.SBCommandReturnObject()
interpreter = lldb.SBDebugger.Create().GetCommandInterpreter()
# use the long syntax to add the menu item rather than +insertInMainMenu since
# the former appears in official documentation
commands = (
'attach %s' % pid,
'expr (BOOL)[(NSBundle *)[NSBundle bundleWithPath:@""] load]',
'expr [(NSMenu *)[(NSApplication *)[NSApplication sharedApplication] mainMenu] addItem:[(FScriptMenuItem *)[FScriptMenuItem new] autorelease]]',
'detach',
'quit',
)
for command in commands:
print(command)
run_command(interpreter, command, result)
tmpfile=$(mktemp -t inject-fscript.lldb)
cat <<EOF | sed -E "s,TARGET,$1," > $tmpfile
attach TARGET
expr (BOOL)[(NSBundle *)[NSBundle bundleWithPath:@""] load]
expr [(NSMenu *)[(NSApplication *)[NSApplication sharedApplication] mainMenu] addItem:[(FScriptMenuItem *)[FScriptMenuItem new] autorelease]]
detach
quit
EOF
lldb -s $tmpfile
rm $tmpfile
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment