Mac OS - add app to autostart with launchctl - macos

I can start application with launchctl like this: launchctl submit -l ProgramName -- open -g -b com.company.ProgramName and it works great! I can kill app, and it'll re-run again even if it crashes.
But when i logout or reboot Mac, my process is no longer run. Or, in some cases, it runs not with ProgramName (whitch is my application name), but with name of executable file in ProgramName.app/Contents/MacOS/program_name
Such situation with plist files. I run command launchctl -w /Users/my_username/Library/LaunchAgents and process works fine! But after restart i can't see it.
I prefer first method to load processes, any suggestions why process doesn't autoload after reboot?

I believe you need to set "RunAtLoad" to "true" for it to start your application when user logs in.
But there is no way to set RunAtLoad from command-line when using "submit". At least not in the version of launchctl.c that I checked online.
It looks like you have to create a .plist file with "load", instead of using "submit".
What is more annoying is that launchctl's "load", can not take stdin as input to .plist filename, either with no-argument, or name "-". Shame, perhaps the developers of launchctl are not from Unix background. This means you are forced to litter your short-lived file somewhere on the file-system, at least until you have it loaded.

Related

How to add launch options to an .arm64 executable without using the terminal on MacOS

I would like to know if someone know how to launch an .arm64 executable with a launch option (in this case "+set fs_game ...") without using a terminal ?
I know that you can do that in windows configuring the .exe but I don't find an alternative for MacOS.
There's a few workarounds, but no actual feature that'd make this clean or easy.
If you have a .app bundle, you can redirect through a shell script that appends some arguments. If you have an Info.plist with this:
<key>CFBundleExecutable</key>
<string>binary</string>
Then you'll want to change it to:
<key>CFBundleExecutable</key>
<string>binary_</string>
And create a Contents/MacOS/binary_ like so:
#!/bin/bash
"$(dirname "$0")/binary" +set fs_game "$#";
And give it a chmod +x.
That'll work no matter how you launch the app, but it will invalidate any existing code signature, so if your app was signed, you'll have to re-sign it.
If you have a standalone binary or if you can't afford to break the code signature, then basically the only option is to make a separate thing that invokes it the way you want. Write a bash script like so:
#!/bin/bash
open -a path/to/your.app --args +set fs_game;
Give it chmod +x and turn it into a separate app. This will obviously only work when invoked via that separate app, but it works with any kind of binary, and without the need to re-sign anything (and the separate app should only show up in your dock for a split second, then vanish again).

Mute Macbook pro when computer is shutting down

I'm thinking of using the Mac's applescript to make a program that mutes the system when it is shutting down.
Though I'm new to applescript and I don't know how to use the IF-statement to determine if the system is shutting down. I've done some googling and I've found that the finder app is the app that is "controlling" the shutdown, but i don't know how to check if the state is "shut down". Can anybody assist me in this matter?
AppleScript has no direct mechanism for detecting a shutdown/logout.
It does have a mechanism for creating applications that can react to themselves being quit.
Thus, you can:
use AppleScript to create a stay-open application (.app bundle) with a standard on quit handler, in which you perform the desired action (
make sure that the application is launched on login - in the simpler case as a Login Item (via System Preferences, see below), or, with more flexibility but complexity, as a launch agent (see https://stackoverflow.com/a/22872222/45375).
Instructions:
Open Script Editor and open a new script window.
Paste the following code:
# This standard handler is called when the application quits.
on quit
# Mute the system volume.
# !! See caveat below.
set volume with output muted
continue quit # signal to the system that it's OK to quit
end quit
Save the script as a stay-open application:
with File Format Application
check Stay open after run handler
Open System Preferences > Users & Groups > Login Items, drag the newly saved *.app bundle into the list, and check the checkbox next to it, so as to make it launch hidden.
The final step is to hide the new application's Dock icon, as there's no reason for it to have one:
From Terminal, run the following:
defaults write /full/path/to/newApp.app/Contents/Info.plist LSUIElement 1
Note: You could use LSBackgroundOnly too, but the advantage of LSUIElement is that you can still display UI elements if you want to, such as for debugging.
Important: Substitute the full path of your new app for /full/path/to/newApp.app; the command will only work if you specify the full path to the Info.plist file.
To test, start the new app interactively, and make sure that no Dock icon appears. (You can quit the app via Activity Monitor).
CAVEAT: If the intent is to suppress the system startup sound, set volume with output muted has two drawbacks:
it will not work if headphones happened to be plugged at the time of shutdown
you will have to unmute the volume on startup (however, you could do that in an on on run handler in the same app).
Consider the alternative approach below, which requires admin privileges to set up and invokes nvram SystemAudioVolume=%80 with root privileges, which bypasses the above drawbacks.
You could run do shell script "nvram SystemAudioVolume=%80" user name "someAdminUsername" password "matchingAdminPassword" with administrator privileges from the above AppleScript app, but you'd have to hard-code the password, which is not advisable for security reasons.
Alternative approach, using a system-wide logout hook via com.apple.loginwindow.
There's a deprecated mechanism for running a script on logout that, however, still works as of OSX 10.10; given that there's no direct non-deprecated equivalent, it may continue to be supported.
Note that you do need admin privileges:
sudo defaults write com.apple.loginwindow LogoutHook <yourScript>
<yourScript> must be an executable, such as a shell script; note that the executable is run in the context of the root user.
In case you're thinking of muting the startup sound, invoke the following shell command from that script:
nvram SystemAudioVolume=%80 # to try this interactively, prepend `sudo `
This will mute sounds until after a reboot, effectively muting the startup sound, without keeping the sound muted.
Note that the nvram command requires root privileges, which are by definition in effect in a script run via the com.apple.loginwindow logout hook.; by contrast, to try the command interactively, use sudo nvram SystemAudioVolume=%80 - otherwise, you'll get the following, unhelpful error message: nvram: Error setting variable - 'SystemAudioVolume': (iokit/common) general error
Honestly, it is better to make a deterministic solution. What I mean is, is that you make a script that:
Mutes your computer.
Shuts it down.
Then you take your script and create an Automator service, that you can assign to some shortcut, to make it easier for you to use it. ctrl-opt-cmd-eject or something. :)
This is just how I would have solved it, if I have the need, it is short and sweet to make work, and should work reasonably well.
If you want to use the LogoutHook mentioned in #mklement0's answer.
You can use the normal Applescript command set volume with output muted.
You just need to add the osascript shebang to the top of the Applescript document
i.e
#!/usr/bin/osascript
set volume with output muted
And then save the file as applescript text file.
In the save dialogue use : file format: Text )
It will get the extension .applescript
Once it is saved, use Terminal.app to chmod the script as you would a normal shell script which in effect it is.
i.e
/bin/chmod +x foo.applescript
Then add it to the loginwindows LogoutHook.
sudo defaults write com.apple.loginwindow LogoutHook foo.applescript
I Know this is an old post but for anyone still looking how to do this(like I was) I have a simple method.
Before I started Scripting I created a new folder in my home folder called toolbar scripts.(this is optional)
With the desktop showing Finder click on Go >Utilities >Script Editor.
In the window that opens type in or copy and paste the code
set volume with output muted
tell application "finder"
shut down
end tell
Click on the last button above the script you added - it should be compile. If you cannot find that button then on the top click on Script >Compile
Click on File >Save in the save as I called mine shutdown and chose the script folder (this is optional)
Down the bottom of the dialog box at file format click on the arrow and change the format to application and click on save.
Open the folder you saved it in and drag the icon to the dock. Click on the icon you just put in the dock.
now if all is right this should mute the volume and shutdown the computer.
This will not shutdown the computer if you still have anything open.
Cheers
Peter
first, you should create a sound-off script (with terminal)
sudo nano /Library/Scripts/sound-off.sh
after filling it with these lines:
#!/bin/bash
osascript -e ‘set volume output muted 1’
and make a sound-on script like that
sudo nano /Library/Scripts/sound-on.sh
and fill it with:
#!/bin/bash
osascript -e ‘set volume 4’
then access them as executing files
sudo chmod u+x /Library/Scripts/sound-off.sh
sudo chmod u+x /Library/Scripts/sound-on.sh
and the last part is set them when the mac device is turn off and on:
sudo defaults write com.apple.loginwindow LogoutHook /Library/Scripts/sound-off.sh
sudo defaults write com.apple.loginwindow LoginHook /Library/Scripts/sound-on.sh

mac osx cant find cronjob which opens safari every few seconds

I´ve installed a cronjob under mac osx which opens/curl an URL. Everything works fine but now I deleted all cronjobs by
crontab -r
and I deleted all lines in the crontab and
crontab -l
does not deliver any entry. The problem is that safari still opens every few seconds. Its also blocks my reboot command. I have to shut it down manually then I can restart my mac. When I get my login screen where I can click my User-Button the mac does a short "flash". So the screen gets white for one short moment. Looks like if you do an screenshot on an Iphone. Safari opens itself on startup (There is no entry in the startupitem list) to execute the Url. Somehow it feels as if there is a cronjob running in the backround but I cannot find it! Thanks.
Try running this script to see if you can spot the curl process being run, then you can find its Parent Process Id (PPID) in column 3, and then see the PID of the calling process. That will tell you how it is getting launched - or by whom, at least.
while :; do ps -aef | egrep "UID|curl" ; sleep 1; done
Ok, I found a solution. I had a .plist in /Library/LaunchAgents calling my URL. I deleted it, remove from trash and restared computer. No it´s gone. Yes.

Managing software updates for daemons -- ensuring they are not running

I have a general question regarding the right way to do software updates on OS X. My application runs two daemons, both as root. One of the daemons can download the software update package from the network. When downloaded it must install the new binaries, including two binaries replacing the current ones for the daemons. Since both the daemons are currently running, replacing them might be a issue. Hence I have few ideas, however it would helpful to get expert advice on the same.
Method 1: After downloading the update, it can be kept in some location. The update daemon can place a bash script in another specified location and create a plist which can be placed in the LaunchDaemons directory to be triggered during the next reboot. The script simply replaces both the binaries before startup. This is possible only if we can ensure that the two daemons do not start up before my script executes. I didn't find a good way to tell launchd to run my script first, before starting the daemons. So I am not quite sure if this is a good solution.
Method 2: After download, immediately launch a script using launchd programatically. This should be possible, right? The script waits for a TERM signal like shown here https://discussions.apple.com/thread/3636062?start=0&tstart=0 to actually replace the binaries and exit gracefully. Again the problem is, when my script gets launched, I must be sure that my daemons are shut down before replacing the binaries. I am not sure how to do this.
Can anyone suggest which is the better approach and also let me know of any better way?
The solution I use to update daemon is to launch separate shell script which do the next:
unload daemon via launchctl unload -w path.to.plist, it will wait for the job to finish.
remove old files
install new files (here I actually install the package, which will launch my daemon in postflight script)
optional: launch daemon with launchctl load -w path.to.plist
To execute script you need just fork(), setsid() and execv()
You can use applescript in your shell script to gracefully quit application:
osascript << EOF
if application "Your Application" is running then
-- Force application to quit
tell application "Your Application" to quit
-- Wait until it quits
repeat
if application "Your Application" is not running then exit repeat
delay 1
end repeat
end if
EOF
killall -15 your_app # just in case

How to launch an app on OS X with command line - The best way

I want to launch an app on OSX from a script. I need to pass some command line arguments. Unfortunately, open doesn't accept command line args.
The only option I can think of is to use nohup myApp > /dev/null & to launch my app so it can exist independently of the script that launches it.
Any better suggestions?
As was mentioned in the question here, the open command in 10.6 now has an args flag, so you can call:
open -n ./AppName.app --args -AppCommandLineArg
In OS X 10.6, the open command was enhanced to allow passing of arguments to the application:
open ./AppName.app --args -AppCommandLineArg
But for older versions of Mac OS X, and because app bundles aren't designed to be passed command line arguments, the conventional mechanism is to use Apple Events for files like here for Cocoa apps or here for Carbon apps. You could also probably do something kludgey by passing parameters in using environment variables.
An application bundle (.app file) is actually a directory. Instead of using open and the .app filename, you can move into the app's directory and start the actual machine code program located inside. For instance:
$ cd /Applications/LittleSnapper.app/
$ ls
Contents
$ cd Contents/MacOS/
$ ./LittleSnapper
That is the actual binary executable that might accept arguments (or not, in LittleSnapper's case).
In case your app needs to work on files (what you would normally expect to pass as: ./myApp *.jpg), you would do it like this:
open *.jpg -a myApp
You can launch apps using open:
open -a APP_YOU_WANT
This should open the application that you want.
open also has an -a flag, that you can use to open up an app from within the Applications folder by it's name (or by bundle identifier with -b flag). You can combine this with the --args option to achieve the result you want:
open -a APP_NAME --args ARGS
To open up a video in VLC player that should scale with a factor 2x and loop you would for example exectute:
open -a VLC --args -L --fullscreen
Note that I could not get the output of the commands to the terminal. (although I didn't try anything to resolve that)
I would recommend the technique that MathieuK offers. In my case, I needed to try it with Chromium:
> Chromium.app/Contents/MacOS/Chromium --enable-remote-fonts
I realize this doesn't solve the OP's problem, but hopefully it saves someone else's time. :)
Lots of complex answers when you can simply access Applications folder and type:
open -a [APP NAME]
This is it!
I wanted to have two separate instances of Chrome running, each using its own profile. I wanted to be able to start them from Spotlight, as is my habit for starting Mac apps. In other words, I needed two regular Mac applications, regChrome for normal browsing and altChrome to use the special profile, to be easily started by keying ⌘-space to bring up Spotlight, then 'reg' or 'alt', then Enter.
I suppose the brute-force way to accomplish the above goal would be to make two copies of the Google Chrome application bundle under the respective names. But that's ugly and complicates updating.
What I ended up with was two AppleScript applications containing two commands each. Here is the one for altChrome:
do shell script "cd /Applications/Google\\ Chrome.app/Contents/Resources/; rm app.icns; ln /Users/garbuck/local/chromeLaunchers/Chrome-swirl.icns app.icns"
do shell script "/Applications/Google\\ Chrome.app/Contents/MacOS/Google\\ Chrome --user-data-dir=/Users/garbuck/altChrome >/dev/null 2>&1 &"
The second line starts Chrome with the alternate profile (the --user-data-dir parameter).
The first line is an unsuccessful attempt to give the two applications distinct icons. Initially, it appears to work fine. However, sooner or later, Chrome rereads its icon file and gets the one corresponding to whichever of the two apps was started last, resulting in two running applications with the same icon. But I haven't bothered to try to fix it — I keep the two browsers on separate desktops, and navigating between them hasn't been a problem.
Beginning with OS X Yosemite, we can now use AppleScript and Automator to automate complex tasks. JavaScript for automation can now be used as the scripting language.
This page gives a good example example script that can be written at the command line using bash and osascript interactive mode. It opens a Safari tab and navigates to example.com.
http://developer.telerik.com/featured/javascript-os-x-automation-example/
osascript -l JavaScript -i
Safari = Application("Safari");
window = Safari.windows[0];
window.name();
tab = Safari.Tab({url:"http://www.example.com"});
window.tabs.push(tab);
window.currentTab = tab;
Simple, here replace the "APP" by name of the app you want to launch.
export APP_HOME=/Applications/APP.app/Contents/MacOS
export PATH=$PATH:$APP_HOME
Thanks me later.
With applescript:
tell application "Firefox" to activate
Why not just set add path to to the bin of the app. For MacVim, I did the following.
export PATH=/Applications/MacVim.app/Contents/bin:$PATH
An alias, is another option I tried.
alias mvim='/Applications/MacVim.app/Contents/bin/mvim'
alias gvim=mvim
With the export PATH I can call all of the commands in the app. Arguments passed well for my test with MacVim. Whereas the alias, I had to alias each command in the bin.
mvim README.txt
gvim Anotherfile.txt
Enjoy the power of alias and PATH. However, you do need to monitor changes when the OS is upgraded.
To Create a New Text File OR open an existing one, in any folder, using a Text/Code Editor like the Free TextMate app on MACOSX, use this command on Terminal:
open -n /Applications/TextMate.app --args "$PWD/some file.txt"
Instead of a Text File, you can use any file type, based on your app's requirements and its support for this syntax.
This command also simulates the New Text Document Here Command on Windows and has been tested on MacBook Pro 2021 and Monterey 12.2.1 successfully.

Resources