Programmatically shut down or sleep sandboxed application Mac - macos

I am trying to make an application for the Mac App Store that will shut down/sleep the Mac after a user-set time. I have tried to use AppleScript, but that won't work if I am going to use it in Sandbox mode, I have tried to Google for a solution, but I cant seem to figure it out.
Hope someone can give me a hint or link to relevant documentation.
edit: made it more precise to what I desire to accomplish.

There is no way to do this. You may try to get a temporary-exception to run "shutdown" or "halt" thru BSD-layer but I doubt that such an app will pass the App Store review as these are tasks that require superuser / admin rights.

I wrote a script once. But I don't know whether it still works. Maybe it gives you at least a clue. Sleep still works - shutdown maybe causes problems if programs are blocking the shutdown.
(Not the prettiest code but it worked for me.)
#/bin/sh
#v1.0.0
#by Sapphire
echo "-----------------------------------------------------"
echo "* Sleep Timer *"
echo "-----------------------------------------------------"
echo "Please enter option: Sleep '1' or shutdown '0'"
echo -n "Option: "
read option
while :
do
if [ $option -eq 1 -o $option -eq 0 ]
then
break
fi
echo "<<ERROR: Only number: 1 or 0 allowed...>>"
echo "Please rnter option: Sleep '1' or shutdown '0'"
echo -n "Option: "
read option
done
echo "Please enter countdown (min):"
echo -n "Minutes: "
read shutDown
while :
do
if [ $shutDown -gt 0 -a $var -eq $var 2> /dev/null ]
then
break
fi
echo "<<ERROR: Positive number expected...>>"
echo "Please enter countdown (min):"
echo -n "Minutes: "
read shutDown
done
echo "*****************************************************"
echo "Counter has been started"
echo "'/!\ Kill countdown with CTRL-C /!\'"
echo -n "Envoking command in: "
date +"%H:%M %Z"
echo "*****************************************************"
if [ $option -eq 0 ]
then
echo "Shutdown in: "
fi
if [ $option -eq 1 ]
then
echo "Sleep in: "
fi
echo -e "\n *------------------------------* "
barCounter=0;
counter=0
((timeBarSize=$shutDown * 2))
bar="||||||||||||||||||||||||||||||"
barEmpty=" "
((flag=0))
for ((i=shutDown*60;i>=0;i--));
do
if ((counter >= timeBarSize ))
then
((counter=0))
((barCounter=$barCounter + 1))
fi
((counter=$counter + 1))
((timehh=$i/3600))
((timemm=$i/60 - timehh*60))
((timess=$i - timemm*60 - timehh*3600))
if (( flag == 1 ))
then
echo $(tput cuu1)$(tput el)$(tput cuu1)$(tput el)$(tput cuu1)$(tput el)$(tput cuu1)$(tput el)$(tput cuu1)
fi
((flag=1))
echo -e " | $timehh:$timemm:$timess |"
echo -e "\r *------------------------------* "
echo -e "\r *${bar:0:$barCounter}${barEmpty:$barCounter:30}* "
echo -e "\r *------------------------------* "
sleep 1;
done
if [ $option -eq 1 ]
then
echo "Going to sleep..."
sleep 1
osascript -e 'tell application "System Events" to sleep'
elif [ $option -eq 0 ];
then
echo "Shutting down..."
sleep 1
osascript -e 'tell application "System Events" to shut down'
else
echo "No valid command found... doing nothing!"
fi
I also had to add this in my '/etc/sudoers' file (CAUTION, if you don't know how to handle this file! -> google):
# Custom privilege
myUser ALL=NOPASSWD: /Users/myUser/Documents/sleepTimer.sh

Related

Bash script for macOS Loop if then else

Trying to do a loop with this script below. It works if I break it by deleting Google Chrome from Applications. If I put Chrome back into place it continuously kills the dock and says No matching processes belonging to you were found. Do I have to add something to killall Dock to exit the script or is Done in the wrong spot? Tried multiple things without any luck. Eventually want it to try every 15 minutes until all apps are in Applications and kill the dock so the shortcuts show instead of Question marks. This will happen once all apps get installed and Dock gets restarted.
i=0
while [ $i -lt 4 ]
do
if [[ -d "/Applications/Company Portal.app" && -d "/Applications/Google Chrome.app" ]];
then
killall Dock
else
i=$((i + 1)) | echo "Will tray again...."
sleep 10
fi
done
Update Here is what I eventually came up with, yea messy! But works! Will have to look at it more after see the responses here though.
echo $(date)
# Check to see if script has already run on computer before, if so, then exit.
file=/Users/Shared/.If_Installed_Restart_Dock_Has_Run.txt
if [ -e "$file" ]; then
echo "The If_Installed_Restart_Dock.sh script has run before and exiting..." && exit 0
else
touch /Users/Shared/.If_Installed_Restart_Dock_Has_Run.txt
fi
i=0
while [ $i -lt 6 ]
do
if [[ -d "/Applications/Google Chrome.app" && -d "/Applications/Microsoft Edge.app" ]];
then
killall Dock && exit 0
else
echo "Applications still need installed in order to restart Dock, will check again in 10 minutes for up to an hour, intune will try again in 8hrs..."
fi
i=$((i + 1))
sleep 600
done
Chrome is irrelevant. You're falling victim to the classic blunder. Consider:
#!/bin/bash
i=0
while [ $i -lt 4 ]; do
if echo "in first if, i = $i"; false ; then
echo bar
else
echo "in else, i = $i"
i=$((i + 1)) | echo "Will tray again...." # (1)
echo "after echo i = $i"
i=$((i + 1))
echo "after 2nd echo i = $i"
fi
done
In the above code, the i=$((i + 1)) in line (1) does not increment the variable used in the control loop. Since that command is in a pipe, it is executed in a subshell, and the variable i in the main shell is not incremented. It would probably be better to structure your code as:
#!/bin/sh
i=0
while [ $((i++)) -lt 4 ]; do
if [ -d "/Applications/Company Portal.app" ] && [ -d "/Applications/Google Chrome.app" ]; then
killall Dock
else
echo "Will tray again...."
sleep 10
fi
done
or
#!/bin/bash
for (( i = 0; i < 4; i++ )); do
if [ -d "/Applications/Company Portal.app" ] && [ -d "/Applications/Google Chrome.app" ]; then
killall Dock
else
echo "Will tray again...."
sleep 10
fi
done

BASH - Timed Input - Show countdown

I have a bash script that asks the user for their details.
I'm setting a limit to how long we wait for the input. I've found this and it appears to what I want.
timelimit=5
echo -e " You have $timelimit seconds\n Enter your name quickly: \c"
name=""
read -t $timelimit name
#read -t $timelimit name <&1
# for bash versions bellow 3.x
if [ ! -z "$name" ]
then
echo -e "\n Your name is $name"
else
echo -e "\n TIME OUT\n You failed to enter your name"
fi
It shows "You have 5 seconds..." any way to update the output so it shows 4,3,2,1 etc as it counts down ?
Thanks
I have tried most of these answers and none of them worked perfectly for me.
Been playing with this for a local developer deployment script.
This solves a few of the issues noted, like including printed output, etc.
Also wrapped as a function for portability. I'm keen to see any improvements.
Here is my solution:
#!/bin/bash
# set -euo pipefail
READTIMEOUT=5
function read_yn {
MESSAGE=$1
TIMEOUTREPLY=$2
NORMALREPLY="Y"
if [ -z "${TIMEOUTREPLY}" ]; then
TIMEOUTREPLY="Y"
fi
TIMEOUTREPLY_UC=$( echo $TIMEOUTREPLY | awk '{print toupper($0)}' )
TIMEOUTREPLY_LC=$( echo $TIMEOUTREPLY | awk '{print tolower($0)}' )
if [ "${TIMEOUTREPLY_UC}" == "Y" ]; then
NORMALREPLY="N"
fi
NORMALREPLY_UC=$( echo $NORMALREPLY | awk '{print toupper($0)}' )
NORMALREPLY_LC=$( echo $NORMALREPLY | awk '{print tolower($0)}' )
for (( i=$READTIMEOUT; i>=0; i--)); do
printf "\r${MESSAGE} [${NORMALREPLY_UC}${NORMALREPLY_LC}/${TIMEOUTREPLY_UC}${TIMEOUTREPLY_LC}] ('${TIMEOUTREPLY_UC}' in ${i}s) "
read -s -n 1 -t 1 waitreadyn
if [ $? -eq 0 ]
then
break
fi
done
yn=""
if [ -z $waitreadyn ]; then
echo -e "\nNo input entered: Defaulting to '${TIMEOUTREPLY_UC}'"
yn="${TIMEOUTREPLY_UC}"
else
echo -e "\n${waitreadyn}"
yn="${waitreadyn}"
fi
}
read_yn "TESTING" "y"
GIST: https://gist.github.com/djravine/7a66478c37974940e8c39764d59d35fa
LIVE DEMO: https://repl.it/#DJRavine/read-input-with-visible-countdownsh
This should work and shouldn't overwrite input, bit more long winded than the other solutions.
#!/bin/bash
abend()
{
stty sane
exit
#Resets stty and then exits script
}
DoAction(){
stty -echo
#Turn off echo
tput sc
#Save cursor position
echo -ne "\033[0K\r"
# Remove previous line
tput cuu1
#Go to previous line
tput el
#clear to end of line
echo "You have $(($time-$count)) seconds"
#Echo timer
echo -n "$Keys"
#Echo currently typed text
stty echo
#turn echo on
tput rc
#return cursor
}
main()
{
trap abend SIGINT # Trap ctrl-c to return terminal to normal
stty -icanon time 0 min 0 -echo
#turn of echo and set read time to nothing
keypress=''
time=5
echo "You have $time seconds"
while Keys=$Keys$keypress; do
sleep 0.05
read keypress && break
((clock = clock + 1 ))
if [[ clock -eq 20 ]];then
((count++))
clock=0
DoAction $Keys
fi
[[ $count -eq $time ]] && echo "you have run out of time" && abend
done
stty sane
echo Your username was $Keys
echo "Thanks for using this script."
exit 0
}
main
This seems to work:
$ cat test.sh
total=5 # total wait time in seconds
count=0 # counter
while [ ${count} -lt ${total} ] ; do
tlimit=$(( $total - $count ))
echo -e "\rYou have ${tlimit} seconds to enter your name: \c"
read -t 1 name
test ! -z "$name" && { break ; }
count=$((count+1))
done
if [ ! -z "$name" ] ; then
echo -e "\nyour name is $name"
else
echo -e "\ntime out"
fi
#!/bin/bash
timelimit=6
name=""
for (( i = 1 ; i <= $timelimit; i++ )); do
echo -ne "\rYou have $(expr $timelimit - $i) seconds. Enter your name quickly: \c"
[ ! -z "$name" ] && { break ; }
read -t 1 name
done
if [ -z "$name" ]; then
echo -e "\n TIME OUT\n You failed to enter your name"
else
echo -e "\n Your name is $name"
fi
this should work
This works fine and fast for me:
#!/bin/bash
#Sets starttimestamp
starttime=$(date +%s)
#Sets timeout
timeout=5
#sets successflag default to false
success=false
#Save Cursorposition
echo -n -e "\033[s"
#While time not up
while [ $(($starttime+$timeout)) -gt $(date +%s) ] ; do
#Return to saved Cursorpositon
echo -n -e "\033[u"
#Display time left
echo "$(((starttime+timeout)-$(date +%s))) seconds left"
#Ask for 1 char then go on in loop make it look like an ongoing input by adding the user variable to the prompt
if read -p foo="Username: $user" -n 1 -t 1 c ; then
#If user hits return in time c will be empty then break out of loop and set success true
if [[ $c == "" ]] ; then
success=true
break
fi
# Append latest character to user variable
user=${user}${c}
unset c
fi
done
if $success ; then
echo "Yiha!"
else
echo "Too late!"
fi

Bash while command

I have created a bash menu script, using options 1-8. For my second menu option I am using the while command to display the current time and date. However, when I press Ctrl+C to end the display of the date it jumps out of the entire script rather than redisplay the menu. Is there a way around this?
yeah, make functions
#more code here ...
display_main_menu() {
while true; do
echo -e "Please select an action:"
echo -e " "
echo -e "1 - Transfer a local file to all servers."
echo -e "2 - Execute a command(s) on all servers."
echo -e "9 - Display all servers."
echo -e "0 - Exit"
echo ""
read -p "" action
case $action in
"1" ) function_to_transfer_file ;;
"2" ) function_to_execute_command ;;
"9" ) function_to_show_server;;
"0" ) exit;;
* ) echo "Please select a valid action.";;
esac
done
}
#more code here ...
Make multiple functions, one for each option in primary menu. Here option 0 exits the script completely.
For secondary menu functions ... option 0 should execute function display_main_menu
You want catch the signal SIGINT (Ctrl+C) and break
trap 'break' SIGINT
For example:
#!/bin/bash
trap 'break' SIGINT
while :
do
sleep 1
echo "foo"
done
echo "bar"
Take a look here to have more details:
http://tldp.org/LDP/Bash-Beginners-Guide/html/chap_12.html
#!/bin/bash
#more code here ...
display_main_menu() {
while true; do
echo -e "Please select an action:"
echo -e " "
echo -e "1 - display time"
echo -e "2 - Execute a command(s) on all servers."
echo -e "9 - Display all servers."
echo -e "0 - Exit"
echo ""
read -p "" action
case $action in
"1" ) trap 'break' SIGINT; while true; do echo "$(date '+%D %T' )"; sleep 1; done ;;
"2" ) function_to_execute_command ;;
"9" ) function_to_show_server;;
"0" ) exit;;
* ) echo "Please select a valid action.";;
esac
done
}
display_main_menu

Preventing a shell script from exiting with Control C

so I am trying to create a script which gives the user some options to do things, one of that option is to exit the script. However, I want to prevent the user from exiting the script using Control c so that the only way to exit is to select the right option. Is it possible to make the script so that when the user hits control c, the program will not exit, but rather it would echo something like "enter 0 to exit"?
#!/bin/bash
# Acts as a simple administration menu
OPTION=0
echo "-----------------Admin Menu-------------------"
echo "Please select one of the following options: "
echo ""
echo "1 - Run top."
echo "2 - Show system uptime."
echo "3 - Show logged in users."
echo "4 - Exit Menu."
echo "5 - Reprint this menu."
echo "----------------------------------------------"
echo "Please choose an option (5 to reprint menu):"
read OPTION
while [ "$OPTION" -ne 4 ]
do
if [ "$OPTION" -eq 1 ]; then
clear
top -n1
elif [ "$OPTION" -eq 2 ]; then
clear
uptime
elif [ "$OPTION" -eq 3 ]; then
clear
who
elif [ "$OPTION" -eq 4 ]; then
clear
OPTION=4
elif [ "$OPTION" -eq 5 ]; then
clear
echo "------------COSC 2306 Admin Menu--------------"
echo "Please select one of the following options: "
echo ""
echo "1 - Run top."
echo "2 - Show system uptime."
echo "3 - Show logged in users."
echo "4 - Exit Menu."
echo "5 - Reprint this menu."
echo "----------------------------------------------"
else
clear
echo "ERROR: Incorrect Input."
fi
echo "Please choose an option (5 to reprint menu):"
read OPTION
done
clear
You can use the trap built-in to catch SIGINT ( ctrl + C generates the signal SIGINT) and print your message:
trap 'echo "enter 0 to exit"' SIGINT
Similarly you can catch other signals too. To get the list of signals use kill -l.

Infinite loop on yes/no ksh

I have an annoying issue that seems to cause and infinite loop and I can't work out why. If I call the following function, it keeps repeating the yes/no options infinitely down the screen until I crash out.
AuditUpload() {
clear
echo "Audit report generated successfully"
echo " "
echo "Do you wish to upload qhub_audit.csv? (1 = Yes/2 = No):"
sleep 1
select yn in "Yes" "No"; do
case $yn in
Yes ) AuditUploader; Auditvi; exit;;
No ) echo "Upload cancelled"; Auditvi; exit;;
esac
done
}
I put the sleep in to see if it would remedy the issue but it still does the same. This issue seems to be very intermittent and doesn't happen every time. This script is written in korn shell (ksh).
AuditUploader function:
AuditUploader() {
echo "Uploading qhub_audit.csv to $HOST..."
curl -v -T qhub_audit.csv -# ftp://xxxxxxxx:xxxxxxxxx#xxxxxxxxxxxxx.com/
if [ "$?" -ne "0" ]
then
echo "ERROR: Cannot upload qhubload.csv"
exit
else
clear
echo "qhub_audit.csv has been put on $HOST successfully"
tput cup 5 5
echo "Copy and paste this link into internet explorer to download:"
tput cup 7 5
echo "ftp://xxxxxxxx:xxxxxxxxx#xxxxxxxxxxxxx.com/qhub_audit.csv"
read LINK
fi
}
Auditvi function:
Auditvi() {
clear
echo "Do you wish to view qhub_audit.csv? (1 = Yes/2 = No):"
sleep 1
select yn in "Yes" "No"; do
case $yn in
Yes ) vi qhub_audit.csv; exit;;
No ) exit;;
esac
done
}
After a bit of playing around it looks like it was looping whenever the 'curl' command returned a specific error which stopped the kill $$ from working properly. I replaced kill $$ with exit 1 and amended the other functions accordingly. I also put in a contingency to use kermit in case the FTP failed. Anyway, this is what my code looks like now:
#########################################
# Upload quotehub audit report function #
#########################################
AuditUploader() {
echo "Uploading qhub_audit.csv to $HOST..."
curl -v -T qhub_audit.csv -# ftp://$USER:$PASSWD#$HOST/ -m 10
if [ "$?" -ne "0" ]
then
echo "ERROR: Cannot upload qhubload.csv via FTP"
if [ ${term} = "tty1A" ]
then
echo "Attempting to download to modems server..."
wermit -s qhub_audit.csv
if [ $? -ne 0 ]
then
echo "Cannot upload to modems either!"
echo "This file will have to be downloaded manually"
exit 1
else
clear
echo "qhub_audit.csv has been put on modems server successfully"
tput cup 5 5
echo "Copy and paste this link into START -> RUN to download:"
tput cup 7 5
echo "\\\\\\xxxxxxxx\download\general\qhub_audit.csv"
read LINK
fi
else
echo "Upload failed!"
exit 1
fi
else
clear
echo "qhub_audit.csv has been put on $HOST successfully"
tput cup 5 5
echo "Copy and paste this link into internet explorer to download:"
tput cup 7 5
echo "ftp://$USER:$PASSWD#$HOST/qhub_audit.csv"
read LINK
fi
}
#######################################################
# Function to prompt user to upload qhub audit report #
#######################################################
AuditUpload() {
clear
echo "Audit report generated successfully"
echo ""
echo "Do you wish to upload qhub_audit.csv? (y/n):"
read REPLY
case "$REPLY" in
Y) AuditUploader; Auditvi; exit;;
y) AuditUploader; Auditvi; exit;;
N) Auditvi; exit;;
n) Auditvi; exit;;
*) echo "invalid option";;
esac
}
######################################
# Function to view qhub audit report #
######################################
Auditvi() {
if [ "$?" -ne "0" ]
then
exit 1
else
clear
echo "Do you wish to view qhub_audit.csv? (y/n):"
read REPLY
case "$REPLY" in
Y) vi qhub_audit.csv; exit;;
y) vi qhub_audit.csv; exit;;
N) exit;;
n) exit;;
*) echo "invalid option"; Pause; Auditvi;;
esac
fi
}
Thanks again guys for all your help.

Resources