1

I have tried using AppleScript to change my startup disk for Boot Camp and Mac. Previously I have tried using terminal (do shell script) but it seems that that option requires me to enter an administrative password.

So:

  1. How do I make it such that I can retrieve the password? I can use keychains.
  2. How do I program the AppleScript/use Terminal through AppleScript to:
    • go into System Preferences/launch it if it is not already open
    • go to the Startup Disk preference pane
    • set the bootdisk to Windows/Mac and have the option to shut down/reboot. (I would like to be able to have a single script to handle everything even if I am restarting into macOS, or shutting down and will use Windows later etc.)

Thanks in advance! (P.S. I have tried searching for some answers and there is a really good answer here over at StackOverflow but it doesn't seem to work on Sierra. Also the other responses I have found on SO and Ask Different don't work.)

The two sets of code I tried (first one using System Preferences, second using Terminal, both using AppleScript):

display dialog "Select a startup disk" buttons ¬
    {"BOOTCAMP", "Macintosh HD"}
if button returned of the result = "BOOTCAMP" then
set bootVol to "BOOTCAMP" as text
else if button returned of the result = "Macintosh HD" then
    set bootVol to "Macintosh HD" as text
end if

do shell script "security 2>&1 >/dev/null find-generic-password -gl \"Insert Password Here\" | awk '{print $2}'"
set myPass to (text 2 thru -2 of result) as text

do shell script "bless -mount \"/Volumes/" & bootVol & ¬
    "\" -setBoot" user name "username" password myPass with administrator privileges

do shell script "shutdown -r now" user name "username" password myPass with administrator privileges

(Insert Password Here refers to the generic key. What I found:

  • when running the command in terminal without backslashes the command works and gives me the password assigned including the quotes like this "Password"

tell application "System Preferences"
    activate
    set the current pane to pane id "com.apple.preference.startupdisk"
    get the name of every anchor of pane id "com.apple.preference.startupdisk"
end tell

try
    tell application "System Events" to tell process "System Preferences" to click button "BOOTCAMP Windows" of radio group 1 of scroll area 1 of group 1 of splitter group 1 of window 1
end try

try
    tell application "System Events" to tell process "System Preferences" to click button "Restart..."
end try

I need to be able to unlock System Preferences, preferably using the same keychain password as I have mentioned in the first script. The second command tell application "System Events" to tell process "System Preferences" to click button "Restart..." does not work.

Brick
  • 1,023
  • Can you clarify what didn't work from the answer you referenced at SO? Also, are you able to share your code - obviously removing any username/password details. It may be that what you already have only needs very minor editing in order for it to work. – Monomeeth Oct 04 '16 at 05:50
  • @Monomeeth When inputting the code into AppleScript and running it stated that on set myPass to getPW() script doesn't understand the error message. I have two sets of code, one trying to use System Preferences, one trying to use Terminal via AppleScript. – Brick Oct 04 '16 at 06:42
  • The part about putting Bootcamp as the volume after receiving bootcamp can be shortened to just copy the returned box but I would like to be able to customise the name if need be in the future. – Brick Oct 04 '16 at 06:55
  • @klanomath I'll assume you meant enabling the following lines separately. Lines 9-10 when activated gives "my_real_password"; lines 11-13 when activated gives The variable BootVol is not defined but substituting it for an actual drive gives the variable myPass is not defined, to which I substituted my password, and it worked, giving "". Enabling lines 14-15 gave "shutdown -r now" after entering in my administrative password. – Brick Oct 04 '16 at 13:56
  • Doing the bless command with administrative permissions with password var myPass returns The administrator user name or password was incorrect. Value of myPass is "Password" including quotes. Lines 9-13 were activated. – Brick Oct 04 '16 at 14:16

2 Answers2

1

In my environment the script works as expected. I used a different name for the generic key though: boot_key

The attributes of boot_key:

enter image description here enter image description here

The password is self-evidently also klanomath's login password. security is always allowed to use the key!


To debug the Apple script enable "Show Log" with cmd3

The script:

display dialog "Select a startup disk" buttons ¬
    {"BOOTCAMP", "Macintosh HD"}
if button returned of the result = "BOOTCAMP" then
    set bootVol to "BOOTCAMP" as text
else if button returned of the result = "Macintosh HD" then
    set bootVol to "Macintosh HD" as text
end if

do shell script "security 2>&1 >/dev/null find-generic-password -gl \"boot_key\" | awk '{print $2}'"
set myPass to (text 2 thru -2 of result) as text

do shell script "bless -mount \"/Volumes/" & bootVol & ¬
    "\" -setBoot" user name "klanomath" password myPass with administrator privileges

do shell script "echo \"shutdown -r now\"" user name "klanomath" password myPass with administrator privileges

will then reveal - after choosing "Macintosh HD" - in the "Replies" part:

tell application "Script Editor"
    display dialog "Select a startup disk" buttons {"BOOTCAMP", "Macintosh HD"}
        --> {button returned:"Macintosh HD"}
end tell
tell current application
    do shell script "security 2>&1 >/dev/null find-generic-password -gl \"boot_key\" | awk '{print $2}'"
        --> "\"Passw0rd\""
    do shell script "bless -mount \"/Volumes/Macintosh HD\" -setBoot" user name "klanomath" password "Passw0rd" with administrator privileges
        --> ""
    do shell script "echo \"shutdown -r now\"" user name "klanomath" password "Passw0rd" with administrator privileges
        --> "shutdown -r now"
end tell
Result:
"shutdown -r now"

You may log various variables explicitly by adding log $variable lines (which is redundant with the "Replies" in some cases):

display dialog "Select a startup disk" buttons ¬
    {"BOOTCAMP", "Macintosh HD"}
if button returned of the result = "BOOTCAMP" then
    set bootVol to "BOOTCAMP" as text
else if button returned of the result = "Macintosh HD" then
    set bootVol to "Macintosh HD" as text
end if
log bootVol

do shell script "security 2>&1 >/dev/null find-generic-password -gl \"boot_key\" | awk '{print $2}'"
set myPass to (text 2 thru -2 of result) as text
log myPass

do shell script "bless -mount \"/Volumes/" & bootVol & ¬
    "\" -setBoot" user name "klanomath" password myPass with administrator privileges
log bootVol
log myPass

do shell script "echo \"shutdown -r now\"" user name "klanomath" password myPass with administrator privileges
log myPass

which then reveals:

tell application "Script Editor"
    display dialog "Select a startup disk" buttons {"BOOTCAMP", "Macintosh HD"}
        --> {button returned:"Macintosh HD"}
end tell
(*Macintosh HD*)
tell current application
    do shell script "security 2>&1 >/dev/null find-generic-password -gl \"boot_key\" | awk '{print $2}'"
        --> "\"Passw0rd\""
    (*Passw0rd*)
    do shell script "bless -mount \"/Volumes/Macintosh HD\" -setBoot" user name "klanomath" password "Passw0rd" with administrator privileges
        --> ""
    (*Macintosh HD*)
    (*Passw0rd*)
    do shell script "echo \"shutdown -r now\"" user name "klanomath" password "Passw0rd" with administrator privileges
        --> "shutdown -r now"
    (*Passw0rd*)
end tell

or as a screenshot:

enter image description here

Now you should be able to detect the error in your script or your environment.

klanomath
  • 66,391
  • 9
  • 130
  • 201
  • Thanks @klanomath for this answer but is it possible to have a dialogue that will prompt for shutdown or reboot right after the bless command, replacing the makeshift shutdown -r now – Brick Oct 05 '16 at 05:36
0

Working solution

The SIP was the first problem I've encountered on Big Sur. Turned it off looks like a bad idea. The second problem was target volumes list items doesn't have actions. Which make impossible to click on them via click or "click at" functions perhaps because of some new additional protections on Big Sur. Click with AST and other scripts also doesn't works due to new MacOS restrictions. The only way I found is using python click(but this leads to a slight delay while script selects target volume).

So here is a fully automated switching:

property targetVolume : "BOOTCAMP" # find name of required volume inside System Preference > Startup Disk
property passwordValue : "yourSystemPassword" # Can be empty

tell application "System Events" tell application "System Preferences" set current pane to pane id "com.apple.preference.startupdisk" activate end tell tell application process "System Preferences" tell window "Startup Disk" set volumePosition to {0, 0} set lockFound to false

        # Check if auth required
        set authButtonText to "Click the lock to make changes."
        if exists button authButtonText then
            click button authButtonText

            # Wait for auth modal
            set unlockButtonText to "Unlock"
            repeat
                if (exists sheet 1) and (exists button unlockButtonText of sheet 1) then exit repeat
            end repeat

            # Autofill password if setted
            if passwordValue is not equal to "" then
                set value of text field 1 of sheet 1 to passwordValue
                click button unlockButtonText of sheet 1
            end if

            # Wait for auth success
            repeat
                if exists button "Click the lock to prevent further changes." then exit repeat
            end repeat
        end if

        # Wait until loading volumes list
        repeat
            if exists group 1 of list 1 of scroll area 1 then exit repeat
        end repeat

        # Click on target volume (posible a slight delay because of shell script executing)
        repeat with m in (UI element of list 1 of scroll area 1)
            if (value of first static text of m = targetVolume) then
                tell static text targetVolume of m
                    set volumePosition to position
                end tell
            end if
        end repeat
        set volumePositionX to item 1 of volumePosition
        set volumePositionY to item 2 of volumePosition
        my customClick(volumePositionX, volumePositionY)

        click button "Restart…"

        # Wait for restart modal appears
        repeat
            if (exists sheet 1) and (exists value of first static text of sheet 1) then exit repeat
        end repeat

        click button "Restart" of sheet 1
    end tell
end tell

end tell

shell script to make click work on target volume

on customClick(x, y) do shell script "

/usr/bin/python <<END

import sys

import time

from Quartz.CoreGraphics import *

def mouseEvent(type, posx, posy):

      theEvent = CGEventCreateMouseEvent(None, type, (posx,posy), kCGMouseButtonLeft)

      CGEventPost(kCGHIDEventTap, theEvent)

def mousemove(posx,posy):

      mouseEvent(kCGEventMouseMoved, posx,posy);

def mouseclick(posx,posy):

      mouseEvent(kCGEventLeftMouseDown, posx,posy);

      mouseEvent(kCGEventLeftMouseUp, posx,posy);

ourEvent = CGEventCreate(None);

currentpos=CGEventGetLocation(ourEvent); # Save current mouse position

mouseclick(" & x & "," & y & ");

mousemove(int(currentpos.x),int(currentpos.y)); # Restore mouse position

END" end customClick

on simpleEncryption(_str) set x to id of _str repeat with c in x set contents of c to c + 100 end repeat return string id x end simpleEncryption

on simpleDecryption(_str) set x to id of _str repeat with c in x set contents of c to c - 100 end repeat return string id x end simpleDecryption

You just need to change two properties targetVolume and passwordValue. Password can be empty and in that case you can provide it manually. Then just copy this script, paste it to the Script Editor and export via File -> Export -> file format - Application, select Run-only -> Save. You can do the same process for all systems you have, for example Big Sur 1, Big Sur 2, Bootcamp.

ratojakuf
  • 111
  • Hi @ratojakuf thanks for your answer. Is there a way to store the password more securely?

    The reason why my original question asked for a method to retrieve a keychain password was because I think it's quite insecure to store a password in a plaintext AppleScript file – all it takes is a rogue programme to read the AppleScript file, or a stranger to physically access your computer while unlocked to discover your system password. Or perhaps I have misunderstood your answer; please clarify with me then. Thanks again :)

    – Brick Sep 04 '22 at 11:18
  • 1
    @brick, Hi, you can add encrypted password like in this answer https://stackoverflow.com/a/14616010/4008381 and then just save apple script as Application – ratojakuf Sep 05 '22 at 13:26