stopping LaunchAgent when uninstalling package - macos

I build an app which provides LaunchAgent (an small UI app with "tray" icon; let's say that defined in /Library/LaunchAgents/foo.plist.
When installing package launchctl loads this LaunchAgent description automatically and starts processes for all logged-in users. (i.e i don't have to load it using launchctl load like i have to do with LaunchDaemons).
When i uninstall package, all files - including LaunchAgent plist file in /Library/LaunchAgents are removed but processes are still running.
(i have several users logged in, so there are several instances of this agent)
Now, how should i tell root launchd to stop these processes (for unspecified number of users) when running as root ? (note, launchctl ran as root doesn't even see those agents).
(pid-files,killall more-or-less-unique-executable-name is last resort and i'm keeping these solution as last resort)

Try this:
LOGGEDUSERS=`who | grep console | awk '{ print $1 }'`
for CURRUSER in $LOGGEDUSERS
do
su -l $CURRUSER -c 'launchctl unload /Library/LaunchAgents/your.plist'
done
In my uninstaller script there is also killall -15 <app_name> after that code (just in case)

Related

How to kill a process that absolutely will not die, OSX terminal

Trying to kill an 2 unwanted process left over by a tech company. They both have to do with remote support. One is in root and the other is in user. Nothing is working.
Let's say the PID is 8005, here's what I have tried in terminal:
kill 8005, kill -9 8005, kill -KILL 8005, sudo kill 8005, sudo kill -9 8005, pkill -p -9 8005, sudo pkill -P -9 8005
And so on. You get the point.
Every time I attempt to kill, it comes back with a new PID almost instantly. The parent is launchd or kernaltask.
The tech company's response is "we have no idea." Please help!
This sounds a lot like Launchd, Apple's launch daemon manager.
You may or may not be able to remove it with the following steps:
Locate the pid of the live process
Enter launchctl list | grep PID into terminal while replacing "PID" with the pid of the process. The PIDs will be listed on the left, if you find the culprit in the list, skip to step 4, if not, continue.
Enter sudo launchctl list | grep PID into terminal and replace "PID" with the pid of the process like you did in step 2, you will most likely find a different list of processes this time because now you are filtering through the root daemons.
If you did not find the process identifier in the list, then the process is not being managed by launchctl (so sorry) and the rest of this answer is pretty irrelevant. If you did, however, find the pid: Continue.
The process should have (on the right side) a name in the format of com.blah.blah or something similar. Make sure to remember this.
If you found the pid in step 2:
Go in Finder, Press CMD+Shift+G and type or paste in ~/Library/LaunchAgents and hit "Go" it will bring you to a folder and look around in that folder for a file named "com.blah.blah" aka the name of the service. If you find the file, head over to the last and final step (at the bottom)
If you found the pid in step 4:
Go in Finder, Press CMD+Shift+G and type or paste in /Library/LaunchDaemons and hit "Go" it will bring you to a folder and look around in that folder for a file named "com.blah.blah" aka the name of the service. If you find the file, head over to the last and final step (at the bottom)
Provided you did not find the daemon in the prev step, repeat the last step only going to /Library/LaunchAgents after pressing "CMD+Shift+G" in Finder.
The final step, the one that makes it real!
At this point, you found the launch agent or daemon, and you're ready to terminate it. When you delete the file, after double checking everything, make sure that either you empty your trash immediately after, or delete it by pressing "CMD+Option+Delete", the reason why we do this, is to make sure that the file is actually gone from the OS, not just moved to the .Trashes folder. Depending on the type of service it is, you may need admin privileges, and also you may need to restart your computer.
Cheers, and good luck!
Edit/PS:
If any of you readers feel compelled to edit this answer/make it more clear, be my guest! I'm still learning the ropes here on SO and am doing my best to help others ;)
I might have a solution. Backup all of your important files, then follow this tutorial to reset your Mac: Click here to learn how to reset your mac back to original settings/data. You will lose your data, but any installed programs will dissapear and you will be able to start over.

How to check for a launched process is loaded or not in MAC OS X from shell script?

I have a launchd process to unload, the command I have used is
launchctl unload /System/Library/LaunchDaemons/costomscript.plist
it works fine if the process is already loaded. But if it is not loaded and I executed the command it gives a message saying something like no such process is loaded. So I need to have a check, if the .plist file is loaded currently then only it should be unloaded otherwise not.
How I can achieve this.. please help. Thanks!!
You can get the information about running processes with launchctl.
One possibility is querying launchd with launchctl list command.
list [-x] [label]
With no arguments, list all of the jobs loaded into launchd in three
columns. The first column
displays the PID of the job if it is running. The second column displays the last exit status
of the job. If the number in this column is negative, it represents the negative of the signal
which killed the job. Thus, "-15" would indicate that the job was terminated with SIGTERM.
The third column is the job's label.
If your plist is loaded, it should be listed, otherwise not. Also first column contains pid of the process, so you could check if the process is running, for example:
$ launchctl list |grep myprocess
600 0 org.example.myprocess.1234
There is also launchctl print command that gives detailed output about a process. Check if you can use it.
print domain-target | service-target
Prints information about the specified service or domain. Domain
output includes various properties about the domain as well as a list
of services and endpoints in the domain with state pertaining to each. Service output includes various properties of the service, including information about its
origin on-disk, its current state, execution context, and last exit status.
For example:
$ launchctl print gui/501/org.example.myprocess.1234 | grep state
state = running
Try:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
$ launchctl print-disabled user/uid
$ launchctl print-disabled user/501
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
And look for "=> true" in your script.
E.g.
$ launchctl print-disabled user/501
disabled services = {
"com.apple.Siri.agent" => true
"com.apple.FileStatsAgent" => false
"com.apple.ScriptMenuApp" => true
login item associations = {
"version.com.docker.helper" => "31259"
"com.docker.helper" => "com.docker.docker"
"version.com.paragon-software.ntfs.FSMenuAppLoginItemHelper" => "15.4.59"

Running a script after user logs in

How could a script wait for the login process to complete before running a command in shell script in Mac OS X?
I have tried wait and sleep commands, but that doesn't seem to stop the script running under the root that owns the login process.
I want the script run after the user logs in.
There are 2 easy(ier) options:
Create a Login Item. To see an example, click here.
Use launchd. You don't have to install anything. All you need to do is to create a configuration file to tell launchd what to do, and save it (with proper permissions) on a specific directory that's read by launchd.

Mac OS X: Refreshing service menus works in Terminal but not in post-install-script

I have an OSX application providing a service menu to other applications. This works fine so far, the menu is activated and available after a user installed the app and logged out and back in. (see similiar post)
I know the log-out-and-back-in is obsolete when triggering the service menu agent (pbs) to scan for changed services by opening a terminal and running
/System/Library/CoreServices/pbs
(this also works without having the new application launched even once)
So I´d like to provide an installer (.pkg) which runs "pbs" as post-install script. Suprisingly, running "bps" as post-install only works if the application was launched before e.g.:
#! /bin/sh
sleep 3
open /Applications/MyApp.app
sleep 3
/System/Library/CoreServices/pbs
exit 0
Unfortunately, (due to application specific reasons) I do not want to start my application directly from the installer. Does anyone know why the post-install-scrips behaves different than the Terminal?
Depending on the rights the Installer requires the post-install scripts maybe runs as root user. Try:
sleep 3
su -l "${USER}" -c "open /Applications/MyApp.app"
sleep 3
su -l "${USER}" -c "/System/Library/CoreServices/pbs"
exit 0
Eventually, as a workarround, I am going to start my application hidden and stopping it again before running pbs. Still looking forward for a better solution.

How can you start a LaunchAgent for the first time without rebooting, when your code runs as a LaunchDaemon?

I have a LaunchDaemon. When it runs, it checks if SIMBL is installed. If SIMBL is not installed, it uses NSTask to run /usr/sbin/installer on the SIMBL.pkg.
SIMBL's postflight script then tries to run a launchctl load command to start SIMBL's LaunchAgent immediately:
sudo -u "$USER" -- /bin/launchctl load -F -S Aqua -D user "${LAUNCHD_PLIST}"
This fails, because my LaunchDaemon's NSTask environment doesn't have $USER set.
If I have my daemon detect the current user with the System Configuration framework and pass it to NSTask with setEnvironment, launchctl bugs out on me:
Bug: launchctl.c:2325 (23930):13: (dbfd = open(g_job_overrides_db_path, O_RDONLY | O_EXLOCK | O_CREAT, S_IRUSR | S_IWUSR)) != -1
I realize a daemon, by definition, should not operate in a user session. By the same token, Apple seems to recommend LaunchAgents as helper objects for LaunchDaemons, to do that user session work. Is there any way to get such an agent up and running immediately?
I have all the .plists in the right places (they start running after a reboot, the next time launchctl does its regular loading) so my first thought was to just tell launchctl to reload. But all the code to do that is commented out in launchctl.c:
// { "reload", reload_cmd, "Reload configuration files and/or directories" },
...
* In later versions of launchd, I hope to load everything in the first pass,
* then do the Bonjour magic on the jobs that need it, and reload them, but for now,
* I haven't thought through the various complexities of reloading jobs, and therefore
* launchd doesn't have reload support right now.
Oh how launchd drives me crazy....
To cut to the chase, after much study and experimentation, this is how I do it on 10.5+:
# If possible, tell launchd to start the LaunchAgent. This will fail on 10.4.
# $F[0] is the pid
# $F[1] is the username
# $F[2] is the first word of the command
ps -ww -A -opid,user,command | \
perl -nae 'if($F[2] =~ /\bloginwindow\b/) { system(
qq(launchctl bsexec $F[0] su $F[1] -c "launchctl load -w <your_plist>"))
}'
I have found no way to achieve this directly on 10.4. I cheat on 10.4 and just run the thing the LaunchAgent would have run, even though it has a GUI and you're not supposed to be able to do that (you can anyway in 10.4-10.6; you can't in 10.7). On 10.4, the LaunchAgent works correct after the next reboot.
The above code looks for loginwindow processes and uses bsexec to run commands in those contexts. Keep in mind that with Fast User Switching, there can be multiple contexts.
Some useful links:
Daemons and Services Programming Guide. You have to read it, but it won't actually answer any of the hard questions. But it will at least give you hints at where everything is located.
TN2083. This is a maddening document that raises as many questions as it answers, but is gospel and mandatory reading for anyone entering the abyss of launchd.
Starting/stopping a launchd agent for all users with GUI sessions. This has several other useful links and explanation.
IMO, launchd is one of the worst "great ideas" Apple has ever deployed. The idea is very useful, but the API is horrible.

Resources