200 lines
9.9 KiB
Markdown
Raw Normal View History

2016-08-18 22:11:42 +03:00
# uninstall
IF YOU CANNOT DESIGN A WORKING `UNINSTALL` STANZA, PLEASE SUBMIT YOUR CASK ANYWAY. The maintainers will help you write an `uninstall` stanza, just ask!
## uninstall pkgutil: Is The Easiest and Most Useful
`pkgutil:` is the easiest and most useful `uninstall` directive. See [Uninstall Key pkgutil:](#uninstall-key-pkgutil).
## uninstall Is Required for Casks That Install a pkg or installer manual:
For most Casks, uninstall actions are determined automatically, and an explicit `uninstall` stanza is not needed. However, a Cask which uses the `pkg` or `installer manual:` stanzas will **not** know how to uninstall correctly unless an `uninstall` stanza is given.
So, while the Cask language does not enforce the requirement, it is much better for end-users if every `pkg` and `installer manual:` has a corresponding `uninstall`.
The `uninstall` stanza is available for non-`pkg` Casks, and is useful for a few corner cases. However, the documentation below concerns the typical case of using `uninstall` to define procedures for a `pkg`.
## There Are Multiple Uninstall Techniques
Since `pkg` installers can do arbitrary things, different techniques are needed to uninstall in each case. You may need to specify one, or several, of the following key/value pairs as arguments to `uninstall`.
## Summary of Keys
* `early_script:` (string or hash) - like `script:`, but runs early (for special cases, best avoided)
* `launchctl:` (string or array) - ids of `launchctl` jobs to remove
* `quit:` (string or array) - bundle ids of running applications to quit
* `signal:` (array of arrays) - signal numbers and bundle ids of running applications to send a Unix signal to (used when `quit:` does not work)
* `login_item:` (string or array) - names of login items to remove
* `kext:` (string or array) - bundle ids of kexts to unload from the system
* `pkgutil:` (string, regexp or array of strings and regexps) - strings or regexps matching bundle ids of packages to uninstall using `pkgutil`
* `script:` (string or hash) - relative path to an uninstall script to be run via sudo; use hash if args are needed
- `executable:` - relative path to an uninstall script to be run via sudo (required for hash form)
- `args:` - array of arguments to the uninstall script
- `input:` - array of lines of input to be sent to `stdin` of the script
- `must_succeed:` - set to `false` if the script is allowed to fail
- `sudo:` - set to `false` if the script does not need `sudo`
* `delete:` (string or array) - single-quoted, absolute paths of files or directory trees to remove. `delete:` should only be used as a last resort. `pkgutil:` is strongly preferred.
* `rmdir:` (string or array) - single-quoted, absolute paths of directories to remove if empty
* `trash:` (string or array) - currently a synonym for `delete:`. In the future this will cause files to be moved to the Trash.
Each `uninstall` technique is applied according to the order above. The order in which `uninstall` keys appear in the Cask file is ignored.
For assistance filling in the right values for `uninstall` keys, there are several helper scripts found under `developer/bin` in the Homebrew-Cask repository. Each of these scripts responds to the `-help` option with additional documentation.
The easiest way to work out an `uninstall` stanza is on a system where the `pkg` is currently installed and operational. To operate on an uninstalled `pkg` file, see [Working With a pkg File Manually](#working-with-a-pkg-file-manually), below.
## uninstall Key pkgutil:
This is the most useful uninstall key. `pkgutil:` is often sufficient to completely uninstall a `pkg`, and is strongly preferred over `delete:`.
IDs for the most recently-installed packages can be listed using the command:
```bash
$ ./developer/bin/list_recent_pkg_ids
```
`pkgutil:` also accepts a regular expression match against multiple package IDs. The regular expressions are somewhat nonstandard. To test a `pkgutil:` regular expression against currently-installed packages, use the command:
```bash
$ ./developer/bin/list_pkg_ids_by_regexp <regular-expression>
```
## List Files Associated With a pkg Id
Once you know the ID for an installed package, (above), you can list all files on your system associated with that package ID using the macOS command:
```bash
$ pkgutil --files <package.id.goes.here>
```
Listing the associated files can help you assess whether the package included any `launchctl` jobs or kernel extensions (kexts).
## uninstall Key launchctl:
IDs for currently loaded `launchctl` jobs can be listed using the command:
```bash
$ ./developer/bin/list_loaded_launchjob_ids
```
IDs for all installed `launchctl` jobs can be listed using the command:
```bash
$ ./developer/bin/list_installed_launchjob_ids
```
## uninstall Key quit:
Bundle IDs for currently running Applications can be listed using the command:
```bash
$ ./developer/bin/list_running_app_ids
```
Bundle IDs inside an Application bundle on disk can be listed using the command:
```bash
$ ./developer/bin/list_ids_in_app </path/to/application.app>
```
## uninstall Key signal:
`signal:` should only be needed in the rare case that a process does not respond to `quit:`.
Bundle IDs for `signal:` targets may be obtained as for `quit:`. The value for `signal:` is an array-of-arrays, with each cell containing two elements: the desired Unix signal followed by the corresponding bundle ID.
The Unix signal may be given in numeric or string form (see the `kill` man page for more details).
The elements of the `signal:` array are applied in order, only if there is an existing process associated the bundle ID, and stopping when that process terminates. A bundle ID may be repeated to send more than one signal to the same process.
It is better to use the least-severe signals which are sufficient to stop a process. The `KILL` signal in particular can have unwanted side-effects.
An example, with commonly-used signals in ascending order of severity:
```ruby
uninstall signal: [
['TERM', 'fr.madrau.switchresx.daemon'],
['QUIT', 'fr.madrau.switchresx.daemon'],
['INT', 'fr.madrau.switchresx.daemon'],
['HUP', 'fr.madrau.switchresx.daemon'],
['KILL', 'fr.madrau.switchresx.daemon'],
]
```
Note that when multiple running processes match the given Bundle ID, all matching processes will be signaled.
Unlike `quit:` directives, Unix signals originate from the current user, not from the superuser. This is construed as a safety feature, since the superuser is capable of bringing down the system via signals. However, this inconsistency may also be considered a bug, and should be addressed in some fashion in a future version.
## uninstall key login_item:
Login items associated with an Application bundle on disk can be listed using the command:
```bash
$ ./developer/bin/list_login_items_for_app </path/to/application.app>
```
Note that you will likely need to have opened the app at least once for any login items to be present.
## uninstall Key kext:
IDs for currently loaded kernel extensions can be listed using the command:
```bash
$ ./developer/bin/list_loaded_kext_ids
```
IDs inside a kext bundle you have located on disk can be listed using the command:
```bash
$ ./developer/bin/list_id_in_kext </path/to/name.kext>
```
## uninstall Key delete:
`delete:` should only be used as a last resort, if other `uninstall` methods are insufficient.
Arguments to `uninstall delete:` should be static, single-quoted, absolute paths.
* Only single quotes should be used.
* Double-quotes should not be used. `ENV['HOME']` and other variables
should not be interpolated in the value.
* Basic tilde expansion is performed on paths, i.e., leading `~` is expanded to the home directory.
* Only absolute paths should be given.
* No glob expansion is performed (*eg* `*` characters are literal), though glob expansion is a desired future feature.
To remove user-specific files, use the `zap` stanza.
## uninstall Key trash:
*stub* - currently a synonym for `delete:`. In the future this will cause files to be moved to the Trash. It is best not to use this stub until it gains the proper functionality.
## Working With a pkg File Manually
Advanced users may wish to work with a `pkg` file manually, without having the package installed.
A list of files which may be installed from a `pkg` can be extracted using the command:
```bash
$ ./developer/bin/list_payload_in_pkg </path/to/my.pkg>
```
Candidate application names helpful for determining the name of a Cask may be extracted from a `pkg` file using the command:
```bash
$ ./developer/bin/list_apps_in_pkg </path/to/my.pkg>
```
Candidate package IDs which may be useful in a `pkgutil:` key may be extracted from a `pkg` file using the command:
```bash
$ ./developer/bin/list_ids_in_pkg </path/to/my.pkg>
```
A fully manual method for finding bundle ids in a package file follows:
1. Unpack `/path/to/my.pkg` (replace with your package name) with `pkgutil --expand /path/to/my.pkg /tmp/expanded.unpkg`.
2. The unpacked package is a folder. Bundle ids are contained within files named `PackageInfo`. These files can be found with the command `find /tmp/expanded.unpkg -name PackageInfo`.
3. `PackageInfo` files are XML files, and bundle ids are found within the `identifier` attributes of `<pkg-info>` tags that look like `<pkg-info ... identifier="com.oracle.jdk7u51" ... >`, where extraneous attributes have been snipped out and replaced with ellipses.
4. Kexts inside packages are also described in `PackageInfo` files. If any kernel extensions are present, the command `find /tmp/expanded.unpkg -name PackageInfo -print0 | xargs -0 grep -i kext` should return a `<bundle id>` tag with a `path` attribute that contains a `.kext` extension, for example `<bundle id="com.wavtap.driver.WavTap" ... path="./WavTap.kext" ... />`.
5. Once bundle ids have been identified, the unpacked package directory can be deleted.