LaunchAgent Script stops at user input window OS X Safari - macos

I'm trying to set up Macs for extreme novice computer users so everything is ready out of the box with the exception of their user account.
The problem with this is there are very hard things to install without access to the user account such as Safari extensions. I have Chrome and Firefox set up - I want to install a couple of basics for them and even clicking the install button when they first load up is going to confuse people, I want it totally hands off.
I'm trying to get Adblocker installed. So far I get it so that the script works perfect when run from terminal but when run as a LaunchAgent it gets the install extension window on Safari requiring user input and stops.
It shouldn't stop as originally I had AppleScript click the menu and then I decided to enable full tab control on keyboard and have cliclick press tab and space (as the user can) which installs too.
As I mentioned, run as a script this works perfectly but for some reason the script stalls when Launchagent runs it on login as soon as the extension install window appears.
Is this a bug, is it an extreme security feature? Do i have to adapt my plist to tell Launchagent not to change the way the script works if windows pop up, do I need more in my script for it to behave differently when run by launchd?
Here is the main part, adapted from bits online.
# HARDCODED VALUE FOR "PATHTOEXTENSION" IS SET HERE
pathToExtension="/ext/safari.safariextz"
# CHECK TO SEE IF A VALUE WAS PASSED IN PARAMETER 4 AND, IF SO, ASSIGN TO "PATHTOEXTENSION"
if [ "$4" != "" ] && [ "$pathToExtension" == "" ];then
pathToExtension=$4
fi
# Error if variable appName is empty
if [ "$pathToExtension" == "" ]; then
echo "Error: No value was specified for the pathToExtension variable..."
exit 1
fi
# Turn on full tab control for keyboard
defaults write NSGlobalDomain AppleKeyboardUIMode -int 3
### Launch Safari if not open
osascript -e 'tell application "Safari"
activate
end tell'
### Prompt the user to install the extension given # $4
osascript -e 'tell application "Safari"
open "'"$pathToExtension"'"
tell application "System Events"
tell process "Safari"
set frontmost to true
end tell
end tell
end tell'
#Run cliclick to press tab and space on the keyboard to install extension
sleep 0.7s
cliclick kp:tab kp:space
### Close Safari
osascript -e 'tell application "System Events"
if ((name of processes) contains "Safari") then
tell application "Safari" to quit
end if
end tell'
### Open Getting Started PDF
osascript -e 'tell application "Preview"
open "/Applications/Tutorials/Quick Start Guide.pdf"
end tell'
### Cleanup - delete launch agent so it doesn't run on next boot
rm ~/Library/LaunchAgents/com.ext.install.plist
### Exit silently, as will error if exists & if times out for whatever reason
exit 0
And the plist to run the script is this...
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.ext.install</string>
<key>Nice</key>
<integer>20</integer>
<key>ProcessType</key>
<string>Interactive</string>
<key>ProgramArguments</key>
<array>
<string>/Users/Shared/install-ext.command</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>StandardErrorPath</key>
<string>/tmp/com.ext.install.err</string>
<key>StandardOutPath</key>
<string>/tmp/com.rxt.install.out</string>
</dict>
</plist>
No errors reported in the logs by the way. Hopefully someone can offer some insight.

Saving this:
install-ext.command
# HARDCODED VALUE FOR "PATHTOEXTENSION" IS SET HERE
pathToExtension="/Users/Shared/ext/wpa.safariextz"
# CHECK TO SEE IF A VALUE WAS PASSED IN PARAMETER 4 AND, IF SO, ASSIGN TO "PATHTOEXTENSION"
if [ "$4" != "" ] && [ "$pathToExtension" == "" ];then
pathToExtension=$4
fi
# Error if variable appName is empty
if [ "$pathToExtension" == "" ]; then
echo "Error: No value was specified for the pathToExtension variable..."
exit 1
fi
### Prompt the user to install the extension given # $4
osascript -e 'tell application "Safari"
activate
open "'"$pathToExtension"'"
end tell
delay 2
tell application "System Events"
tell process "Safari"
click button "Install" of window 1
end tell
end tell'
### Exit silently, as will error if exists & if times out for whatever reason
exit 0
I tell Safari to click the Install button. Rather than use a third party app.
Chmod'd the file to allow it to be an executable.
Used your LaunchAgent as is.
Manually loading the LaunchAgent ran the code. But as expected I also got the prompt to authorise install-ext.command to control the computer.
So I had to auth. That first.
Then any subsequent load of the LaunchAgent worked as desired including on login.

Related

Apple script open Terminal with ready command

I'm trying to open terminal using apple script with a ready command but without executing it and allowing user to do this just by clicking enter (so I don't want to use tell Terminal to do script)
One of the approaches I used is using keystrokes:
tell application "Terminal" do script "echo Hi!"
keystroke "abc"
end tell
but it doesn't work for me. Any ideas?
I think you want to start Terminal and have a command all lined up ready in the Terminal ready for the user so he/she only has to press "Enter". If so, you can do this:
tell application "Terminal"
activate
delay 1
tell application "System Events"
keystroke "echo hi"
end tell
end tell
Then the user just has to press Enter and the command echo hi will execute.
it is hard to understand what you mean.
You can't for instance have the terminal wait for a user to click its window.
(But you can poll for a keystroke after the terminal window is opened.)
You'd have to use a dialog before your code, in order to make the user enter the terminal consciously.
display dialog "Press ok to enter the terminal" buttons {"Cancel","Enter"} cancel button 1 default button 2
Other than that, the way you'd need to use system events to send keystroke to the Terminal
tell application "System Events"
tell application process "Terminal"
keystroke "abcd"
end tell
end tell
You can poll for a keypress in the do script command to your terminal with this:
read -n 1 -s MYCHAR </dev/tty
This will force the user to press enter from a do script
a=`read`

Script to detect if NAS is connected and if so mount Shares

I'm trying to find a way of mounting Shares (afp) on my NAS automatically. I use logins on it to control which shares are accessible by the computer (for privacy and other reasons). When switching logins not all of the shares get re-mounted which causes problems for some of the applications I run.
What I want to do is have a script which would run every time I login to the NAS (even if it's just guest login) then this script would mount the shares.
I was wondering if anyone could point me in the right direction. This is on an OS X computer so was thinking of using applescript to achieve this.
Thanks in advance
Tom
I have been fighting this problem on my mac for ages and believe i finally have a solution.
I have split it into two scripts:
the first one (exported in applescript editor as stay open application) runs on idle (rather than a repeat loop) and calls a second script every 10 seconds that handles the drive mounting. the errors that i check for in the first script are quite important as -128 ensures you can still quit the stay open script with right click or on osx shutdown, while the -5014 is an unknown error that unless explicitly handled pops up a dialog in my case.
--------------------------------------------------------------------------------------
--"On Idle Launch Basic Drive Mounter.app"
on idle
try
--script loads on startup, so first we wait 5 seconds to ensure network
delay 5
--run the mounter script which is on the desktop
run script file ":Users:localusername:Desktop:Basic Drive Mounter.app"
on error errStr number errorNumber
--listen for the apple quit command and quit
if the errorNumber is equal to -128 then
quit
return 1
--listen for the unknown error and ignore it
else if the errorNumber is equal to -5014 then
return 5
else
--all other errors are also ignored
return 5
end if
end try
--return with a wait of 5 seconds before next idle run
return 5
end idle
--------------------------------------------------------------------------------------
the second script (export as an application) does the checking of the network, then tries to mount the volume using a shell mount. i originally used a finder "mount volume" and that code exists as inline comments, but I didn't like the dialog popping up on errors; even if only for a second, so i moved on to shell script.
--------------------------------------------------------------------------------------
--"Basic Drive Mounter.app"
try
set IP_address to "xxx.xxx.xxx.xxx"
set IP_Valid to true
try
do shell script ("ping -c 2 " & IP_address)
on error
set IP_Valid to false
end try
if IP_Valid then
tell application "Finder"
if disk "work" exists then
else
try
do shell script "mkdir /Volumes/work"
end try
do shell script "mount_afp afp://xxx.xxx.xxx.xxx/work /Volumes/work/"
-->>finder mount volume version
--with timeout of 1 second
-- mount volume "afp://xxx.xxx.xxx.xxx/work"
--end timeout
--<<finder mount volume version
end if
end tell
end if
on error
return 0
-->>finder mount volume version
--on error finder returns an error dialog which needs to be closed to go back and retry
--tell application "System Events"
-- keystroke return
--end tell
--<<finder mount volume version
end try
--------------------------------------------------------------------------------------
once you have it working, drag the first script/app into your users login items, to have it automatically started when you log in. if you don't need the persistent remapping, then drag the second script/app into the login items for a single run.
The basic idea would be to make a launchd agent to watch a folder for changes. You will want to watch the /Volumes folder because when you login to your NAS something will get mounted in the Volumes folder. Thus the watch agent will detect that something changed in the Volumes folder and it will run a script.
It's very simple. You can google about launchd and find many examples. But to setup a watch folder use something like this...
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>WatchPaths</key>
<array>
<string>/Volumes</string>
</array>
<key>ProgramArguments</key>
<array>
<string>/usr/bin/osascript</string>
<string>/path/to/applescript</string>
</array>
<key>Label</key>
<string>com.someName.plistFileName</string>
</dict>
</plist>
So just create a text file with the above code. Save it with an extension of ".plist". Insert a path to the applescript in the ProgramArguments section and give it a name in the Label section.
Put this plist in ~/Library/LaunchAgents folder and restart your computer. Now every time something changes in the /Volumes folder the applescript will be run.
Then you just create the applescript properly. You will first need to check the Volumes folder and see if your NAS is mounted. If it is then mount any additional shares you want and if not then do nothing. You can google (or search stack overflow) how to mount shares using applescript.
Good luck.

How do I convert this AppleScript to osascript?

I have written an AppleScript and want to convert it to an osascript so I can run it on launch using launchd. Is there any way I can convert this to osascript or do I have to rewrite the whole script as an osascript? If it can't be done is there at least a way to run it as osascript in the terminal? Thank you!
on idle
tell application "System Events" to ¬
if exists process "Launchpad" then run script
tell application "Launchpad"
delay 0
tell application "System Events" to keystroke "b" using {control down, option down, command down}
delay 0
tell application "System Events" to keystroke "b" using {control down, option down, command down}
delay 0
tell application "System Events" to keystroke "b" using {control down, option down, command down}
delay 0
end tell
end idle
If you only need to run it once per second or less frequently, you could save it as a normal script:
tell application "System Events"
if not (exists process "Launchpad") then return
repeat 3 times
keystroke "b" using {control down, option down, command down}
end repeat
end tell
And repeat running the script with launchd:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC -//Apple Computer//DTD PLIST 1.0//EN
http://www.apple.com/DTDs/PropertyList-1.0.dtd>
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.stackoverflow.11945633</string>
<key>ProgramArguments</key>
<array>
<string>osascript</string>
<string>/Users/username/Library/Scripts/script.scpt</string>
</array>
<key>StartInterval</key>
<integer>1</integer>
</dict>
</plist>
The property list has to be loaded manually with launchctl load ~/Library/LaunchAgents/com.stackoverflow.11945633.plist. Applying changes requires unloading and loading it.
Programs are sent a SIGKILL signal after 20 seconds by default. You can override the timeout by adding an ExitTimeOut key. See man launchd.plist.
The script doesn't actually work for changing the Launchpad background. Launchpad.app is just a dummy application that quits immediately when it's opened.
If you just want to change the background style, you can do it with defaults write com.apple.dock springboard-background-filter -int 2; killall Dock.

Script to shutdown mac

I'm trying to automate the shutdown of my mac, I've tried the scheduled shutdown in energy saver and I wanna sleep but these don;t seem to work. VLC player runnign seems to prevent the shutdown. I think I need a script to forcefully shutdown the mac regardless of of what errors may thrown to screen by various programs running.
Thanks
Ok,
This is the applescript code im using to shutdown may mac. I've added it as an iCal event thats runs nightly.
tell application "System Events" to set the visible of every process to true
set white_list to {"Finder"}
try
tell application "Finder"
set process_list to the name of every process whose visible is true
end tell
repeat with i from 1 to (number of items in process_list)
set this_process to item i of the process_list
if this_process is not in white_list then
do shell script "killall \"" & this_process & "\""
end if
end repeat
on error
tell the current application to display dialog "An error has occurred!" & return & "This script will now quit" buttons {"Quit"} default button 1 with icon 0
end try
tell application "System Events"
shut down
end tell
Could you try a simple applescript, which goes something like this...
tell application "System Events"
shut down
end tell
See if it works, and then you can make it run through Automator at certain time, etc.
my solution (somwhat late). Just a bash script with apple in it:
#!/bin/bash
# OK, just shutdown all ... applications after n minutes
sudo shutdown -h +2 &
# Try normal shutdown in the meantime
osascript -e 'tell application "System Events" to shut down'
I also edited the /etc/sudoers (and /private/etc/sudoers) file(s) and added the line:
ALL=NOPASSWD: /sbin/shutdown
Always worked for me for an assured shutdown (knock knock ;-) )
This should do:
do shell script "shutdown" with administrator privileges
If you want to pass the admin password from key chain, with no prompt:
do shell script "shutdown" with administrator privileges password "password here"
But do not store the admin password in clear anywhere. Instead use the keychain access.
Alternatively you could kill all user processes, via:
do shell script "kill -9 -1"
This however would also kill your own Applescript process, preventing it from requesting the shutdown/restart afterwards.
Either way you're playing with fire, when using sudo or kill.
do what linux users do. use a bash script. if u dont know how to create one just go ahead and download ANY bash script u find using your internet search and open it with text edit app and paste the following:
( be careful if many people use the pc , then this method is not recommended, cause they can learn your user login password from inside this script )
#!/bin/bash
echo -n "Enter a number > "
read x
echo [your password] | sudo -S shutdown -h +$x
it will work the same way it works in linux. the terminal will pop up a message and ask you to enter a number. if we choose for exaple 50 , then the pc ( niresh ) or mac will shutdown in 50 minutes.

Sending commands and strings to Terminal.app with Applescript

I want to do something like this:
tell application "Terminal"
activate
do script "ssh user#server.com"
-- // write user's password
-- // write some linux commands to remote server
end tell
For example to log in to the server, enter the password, and then login to mysql and select a DB.
I type that every day and it would be really helpful to bundle it into a script.
Also, is there a reference of what commands, properties, functions, etc. do applications (Terminal, Finder, etc) have available to use within Applescript? thanks!
EDIT: Let me clear this up:
I don't want to do several 'do script' as I tried and doesn't work.
I want to open a Terminal window, and then emulate a human typing in some characters and hitting enter. Could be passwords, could be commands, whatever, just sending chars to the Terminal which happens to be running ssh. I tried keystroke and doesn't seem to work.
First connect to the server and wait for 6 seconds (you can change that) and then execute whatever you need on the remote server using the same tab
tell application "Terminal"
set currentTab to do script ("ssh user#server;")
delay 6
do script ("do something remote") in currentTab
end tell
As EvanK stated each do script line will open a new window however you can run two commands with the same do script by separating them with a semicolon. For example:
tell application "Terminal"
do script "date;time"
end tell
But the limit appears to be two commands.
However, you can append "in window 1" to the do script command (for every do script after the first one) to get the same effect and continue to run as many commands as you need to in the same window:
tell application "Terminal"
do script "date"
do script "time" in window 1
do script "who" in window 1
end tell
Note that I just used the who, date, and time command as an example...replace
with whatever commands you need.
Here's another way, but with the advantage that it launches Terminal, brings it to the front, and creates only one window.
I like this when I want to be neatly presented with the results of my script.
tell application "Terminal"
activate
set shell to do script "echo 1" in window 1
do script "echo 2" in shell
do script "echo 3" in shell
end tell
How about this? There's no need for key codes (at least in Lion, not sure about earlier), and a subroutine simplifies the main script.
The below script will ssh to localhost as user "me", enter password "myPassw0rd" after a 1 second delay, issue ls, delay 2 seconds, and then exit.
tell application "Terminal"
activate
my execCmd("ssh me#localhost", 1)
my execCmd("myPassw0rd", 0)
my execCmd("ls", 2)
my execCmd("exit", 0)
end tell
on execCmd(cmd, pause)
tell application "System Events"
tell application process "Terminal"
set frontmost to true
keystroke cmd
keystroke return
end tell
end tell
delay pause
end execCmd
You don't need to "tell" Terminal to do anything. AppleScript can do shell scripts directly.
set theDir to "~/Desktop/"
do shell script "touch " & theDir &"SomeFile.txt"
or whatever ...
Why don't use expect:
tell application "Terminal"
activate
set currentTab to do script ("expect -c 'spawn ssh user#IP; expect \"*?assword:*\"; send \"MySecretPass
\"; interact'")
end tell
Your question is specifically about how to get Applescript to do what
you want. But, for the particular example described, you might want
to look into 'expect' as a solution.
Kinda related, you might want to look at Shuttle (http://fitztrev.github.io/shuttle/), it's a SSH shortcut menu for OSX.
The last example get errors under 10.6.8 (Build 10K549) caused by the keyword "pause".
Replacing it by the word "wait" makes it work:
tell application "Terminal"
activate
my execCmd("ssh me#localhost", 1)
my execCmd("myPassw0rd", 0)
my execCmd("ls", 2)
my execCmd("exit", 0)
end tell
on execCmd(cmd, wait)
tell application "System Events"
tell application process "Terminal"
set frontmost to true
keystroke cmd
keystroke return
end tell
end tell
delay wait
end execCmd
I could be mistaken, but I think Applescript Terminal integration is a one-shot deal...That is, each do script call is like opening a different terminal window, so I don't think you can interact with it at all.
You could copy over the SSH public keys to prevent the password prompt, then execute all the commands joined together (warning: the following is totally untested):
tell application "Terminal"
activate
do script "ssh jdoe#example.com '/home/jdoe/dosomestuff.sh && /home/jdoe/dosomemorestuff.sh'"
end tell
Alternatively, you could wrap the ssh and subsequent commands in a shell script using Expect, and then call said shell script from your Applescript.
set up passwordless ssh (ssh-keygen, then add the key to ~/.ssh/authorized_keys on the server). Make an entry in ~/.ssh/config (on your desktop), so that when you run ssh mysqlserver, it goes to user#hostname... Or make a shell alias, like gotosql, that expands to ssh user#host -t 'mysql_client ...' to start the mysql client interactively on the server.
Then you probably do need someone else's answer to script the process after that, since I don't know how to set startup commands for mysql.
At least that keeps your ssh password out of the script!
Petruza,
Instead of using keystroke use key code.
The following example should work for you.
tell application "System Events"
tell application process "Terminal"
set frontmost to true
key code {2, 0, 17, 14}
keystroke return
end tell
end tell
The above example will send the characters {d a t e}
to Terminal and then keystroke return will enter and run
the command. Use the above example with whatever key codes you need
and you'll be able to do what you're trying to do.
what about something like this:
tell application "Terminal"
activate
do shell script "sudo dscl localhost -create /Local/Default/Hosts/cc.josmoe.com IPAddress 127.0.0.1"
do shell script "sudo dscl localhost -create /Local/Default/Hosts/cc.josmos2.com IPAddress 127.0.0.1"
end tell
As neat solution, try-
$ open -a /Applications/Utilities/Terminal.app *.py
or
$ open -b com.apple.terminal *.py
For the shell launched, you can go to Preferences > Shell > set it to exit if no error.
That's it.
I built this script. It is in Yosemite and it is bash script using AppleScript to choose a list of users for SSH servers. Basically you define an IP and then the user names.. when the application launches it asks who you want to login in as.. the SSH terminal is launched and logged in prompting a password...
(***
* --- --- --- --- ---
* JD Sports Fashion plc
* Apple Script
* Khaleel Mughal
* --- --- --- --- ---
* #SHELLSTAGINGSSHBASH
* --- --- --- --- ---
***)
set stagingIP to "192.162.999.999"
set faciaName to (choose from list {"admin", "marketing", "photography_cdn"})
if faciaName is false then
display dialog "No facia was selected." with icon stop buttons {"Exit"} default button {"Exit"}
else
set faciaName to (item 1 of faciaName)
tell application "Terminal"
activate
do script "ssh " & faciaName & "#" & stagingIP & ""
end tell
end if
I highly recommend though; Nathan Pickmans post above about Shuttle (http://fitztrev.github.io/shuttle/).. a very smart and simple application.

Resources