How to extract .pkg files (and Payload)
Published 2021-11-9Let's say you've got a package foo.pkg
and you'd like to extract it to /tmp/foo
to find out what's inside, you could do this:
# delete /tmp/foo, if it exists
rm -rf /tmp/foo
# extract foo.pkg
pkgutil --expand-full ~/Downloads/foo.pkg /tmp/foo/
The secret of --expand-full is that it will also decompress Payload
files within the .pkg
.
Rationale
It makes me nervous when a program that I want to use requires sudo
(Admin) privileges to install.
Simple user programs (as opposed to Kernel drivers) should NOT require admin access to my computer.
In particular, I found that both OBS Advanced Scene Switcher (which is unsigned) and gpg
require admin privileges to install, and that just goes against all reason - so I wanted to open up the pkg to figure out what kind of kool-aid they were trying to get me to drink - but it turns out neither need all the hoopla.
They, like most programs, can install into user space just fine.
Note: My kool-aid free install script for gpg is up on https://webinstall.dev/gpg.
Example GnuPG
Here's what's inside of the GnuPG package for macOS;
rm -rf /tmp/GnuPG
pkgutil --expand-full /Volumes/GnuPG\ 2.2.33/Install.pkg /tmp/GnuPG
lsd --tree /tmp/GnuPG
GnuPG
├── Distribution
└── GnuPG.pkg
├── Bom
├── PackageInfo
├── Payload
│ ├── bin
│ │ ├── convert-keyring
│ │ ├── dirmngr
│ │ ├── dirmngr-client
│ │ ├── dumpsexp
│ │ ├── gpg ⇒ gpg2
│ │ ├── gpg-agent
│ │ ├── gpg-connect-agent
│ │ ├── gpg-error
│ │ ├── gpg2
│ │ ├── gpgconf
│ │ ├── gpgme-json
│ │ ├── gpgparsemail
│ │ ├── gpgsm
│ │ ├── gpgtar
│ │ ├── gpgv2
│ │ ├── hmac256
│ │ ├── kbxutil
│ │ ├── mpicalc
│ │ ├── pinentry-mac.app
│ │ │ └── Contents
│ │ │ ├── Info.plist
│ │ │ ├── MacOS
│ │ │ │ └── pinentry-mac
│ │ │ └── Resources
│ │ │ ├── de.lproj
│ │ │ │ └── Localizable.strings
│ │ │ ├── en.lproj
│ │ │ │ └── Localizable.strings
│ │ │ ├── Icon.icns
│ │ │ ├── Main.nib
│ │ │ └── Pinentry.nib
│ │ └── watchgnupg
│ ├── etc
│ ├── lib
│ │ ├── libasprintf.0.dylib
│ │ ├── libasprintf.dylib ⇒ libasprintf.0.dylib
│ │ ├── libassuan.0.dylib
│ │ ├── libassuan.dylib ⇒ libassuan.0.dylib
│ │ ├── libcharset.1.dylib
│ │ ├── libcharset.dylib ⇒ libcharset.1.dylib
│ │ ├── libgcrypt.20.dylib
│ │ ├── libgcrypt.dylib ⇒ libgcrypt.20.dylib
│ │ ├── libgpg-error.0.dylib
│ │ ├── libgpg-error.dylib ⇒ libgpg-error.0.dylib
│ │ ├── libgpgme.11.dylib
│ │ ├── libgpgme.dylib ⇒ libgpgme.11.dylib
│ │ ├── libgpgmepp.6.dylib
│ │ ├── libgpgmepp.dylib ⇒ libgpgmepp.6.dylib
│ │ ├── libhistory.8.1.dylib
│ │ ├── libhistory.8.dylib ⇒ libhistory.8.1.dylib
│ │ ├── libhistory.dylib ⇒ libhistory.8.1.dylib
│ │ ├── libiconv.2.dylib
│ │ ├── libiconv.dylib ⇒ libiconv.2.dylib
│ │ ├── libintl.8.dylib
│ │ ├── libintl.dylib ⇒ libintl.8.dylib
│ │ ├── libksba.8.dylib
│ │ ├── libksba.dylib ⇒ libksba.8.dylib
│ │ ├── libnpth.0.dylib
│ │ ├── libnpth.dylib ⇒ libnpth.0.dylib
│ │ ├── libntbtls.0.dylib
│ │ ├── libntbtls.dylib ⇒ libntbtls.0.dylib
│ │ ├── libreadline.8.1.dylib
│ │ ├── libreadline.8.dylib ⇒ libreadline.8.1.dylib
│ │ ├── libreadline.dylib ⇒ libreadline.8.1.dylib
│ │ ├── libsqlite3.0.dylib
│ │ ├── libsqlite3.dylib ⇒ libsqlite3.0.dylib
│ │ ├── libtextstyle.0.dylib
│ │ └── libtextstyle.dylib ⇒ libtextstyle.0.dylib
│ ├── libexec
│ │ ├── dirmngr_ldap
│ │ ├── gpg-check-pattern
│ │ ├── gpg-preset-passphrase
│ │ ├── gpg-protect-tool
│ │ ├── gpg-wks-client
│ │ └── scdaemon
│ └── share
│ ├── gnupg
│ │ ├── distsigkey.gpg
│ │ │ └── distsigkey.gpg
│ │ ├── help.be.txt
│ │ ├── help.ca.txt
│ │ ├── help.cs.txt
│ │ ├── help.da.txt
│ │ ├── help.de.txt
│ │ ├── help.el.txt
│ │ ├── help.eo.txt
│ │ ├── help.es.txt
│ │ ├── help.et.txt
│ │ ├── help.fi.txt
│ │ ├── help.fr.txt
│ │ ├── help.gl.txt
│ │ ├── help.hu.txt
│ │ ├── help.id.txt
│ │ ├── help.it.txt
│ │ ├── help.ja.txt
│ │ ├── help.nb.txt
│ │ ├── help.pl.txt
│ │ ├── help.pt.txt
│ │ ├── help.pt_BR.txt
│ │ ├── help.ro.txt
│ │ ├── help.ru.txt
│ │ ├── help.sk.txt
│ │ ├── help.sv.txt
│ │ ├── help.tr.txt
│ │ ├── help.txt
│ │ ├── help.zh_CN.txt
│ │ ├── help.zh_TW.txt
│ │ └── sks-keyservers.netCA.pem
│ └── man
│ ├── man1
│ │ ├── autopoint.1
│ │ ├── dirmngr-client.1
│ │ ├── envsubst.1
│ │ ├── gettext.1
│ │ ├── gettextize.1
│ │ ├── gpg-agent.1
│ │ ├── gpg-check-pattern.1
│ │ ├── gpg-connect-agent.1
│ │ ├── gpg-preset-passphrase.1
│ │ ├── gpg-wks-client.1
│ │ ├── gpg-wks-server.1
│ │ ├── gpg.1
│ │ ├── gpgconf.1
│ │ ├── gpgparsemail.1
│ │ ├── gpgrt-config.1
│ │ ├── gpgsm.1
│ │ ├── gpgtar.1
│ │ ├── gpgv.1
│ │ ├── hmac256.1
│ │ ├── iconv.1
│ │ ├── msgattrib.1
│ │ ├── msgcat.1
│ │ ├── msgcmp.1
│ │ ├── msgcomm.1
│ │ ├── msgconv.1
│ │ ├── msgen.1
│ │ ├── msgexec.1
│ │ ├── msgfilter.1
│ │ ├── msgfmt.1
│ │ ├── msggrep.1
│ │ ├── msginit.1
│ │ ├── msgmerge.1
│ │ ├── msgunfmt.1
│ │ ├── msguniq.1
│ │ ├── ngettext.1
│ │ ├── recode-sr-latin.1
│ │ ├── scdaemon.1
│ │ ├── sqlite3.1
│ │ ├── watchgnupg.1
│ │ └── xgettext.1
│ ├── man3
│ │ ├── bind_textdomain_codeset.3
│ │ ├── bindtextdomain.3
│ │ ├── dcgettext.3
│ │ ├── dcngettext.3
│ │ ├── dgettext.3
│ │ ├── dngettext.3
│ │ ├── gettext.3
│ │ ├── history.3
│ │ ├── iconv.3
│ │ ├── iconv_close.3
│ │ ├── iconv_open.3
│ │ ├── iconv_open_into.3
│ │ ├── iconvctl.3
│ │ ├── ngettext.3
│ │ ├── readline.3
│ │ └── textdomain.3
│ ├── man7
│ │ └── gnupg.7
│ └── man8
│ ├── addgnupghome.8
│ ├── applygnupgdefaults.8
│ └── dirmngr.8
└── Scripts
├── postinstall
└── preinstall
Rather than installing to a system location, this can now be installed to ~/.local/opt/gnupg
as it ought to be:
mkdir -p ~/.local/opt/gnupg/
rsync -avhP /tmp/GnuPG/GnuPG.pkg/Payload/ ~/.local/opt/gnupg/
Note: since the some assumptions about the location of certain files have been baked in at compile-time (rather than being read relative at run-time), some paths need to be added manually to ~/.gnupg/gpg-agent.conf
and ~/Library/LaunchAgents/gpg-agent.plist
.
That's covered in my other article at https://coolaj86.com/archive/.
Example: Scene Switcher
Here's what's the inside of SceneSwitcher.pkg
(the OBS Advanced Scene Switcher (github) plugin):
rm -rf /tmp/SceneSwitcher
pkgutil --expand-full ~/Downloads/SceneSwitcher/MacOs/SceneSwitcher.pkg /tmp/SceneSwitcher
lsd --tree /tmp/SceneSwitcher
SceneSwitcher
├── Bom
├── PackageInfo
├── Payload
│ └── Library
│ └── Application Support
│ └── obs-studio
│ └── plugins
│ └── advanced-scene-switcher
│ ├── bin
│ │ ├── advanced-scene-switcher.so
│ │ ├── libopencv_calib3d.4.5.3.dylib
│ │ ├── libopencv_calib3d.4.5.dylib ⇒ libopencv_calib3d.4.5.3.dylib
│ │ ├── libopencv_calib3d.dylib ⇒ libopencv_calib3d.4.5.dylib
│ │ ├── libopencv_core.4.5.3.dylib
│ │ ├── libopencv_core.4.5.dylib ⇒ libopencv_core.4.5.3.dylib
│ │ ├── libopencv_core.dylib ⇒ libopencv_core.4.5.dylib
│ │ ├── libopencv_features2d.4.5.3.dylib
│ │ ├── libopencv_features2d.4.5.dylib ⇒ libopencv_features2d.4.5.3.dylib
│ │ ├── libopencv_features2d.dylib ⇒ libopencv_features2d.4.5.dylib
│ │ ├── libopencv_flann.4.5.3.dylib
│ │ ├── libopencv_flann.4.5.dylib ⇒ libopencv_flann.4.5.3.dylib
│ │ ├── libopencv_flann.dylib ⇒ libopencv_flann.4.5.dylib
│ │ ├── libopencv_imgproc.4.5.3.dylib
│ │ ├── libopencv_imgproc.4.5.dylib ⇒ libopencv_imgproc.4.5.3.dylib
│ │ ├── libopencv_imgproc.dylib ⇒ libopencv_imgproc.4.5.dylib
│ │ ├── libopencv_objdetect.4.5.3.dylib
│ │ ├── libopencv_objdetect.4.5.dylib ⇒ libopencv_objdetect.4.5.3.dylib
│ │ └── libopencv_objdetect.dylib ⇒ libopencv_objdetect.4.5.dylib
│ └── data
│ ├── locale
│ │ ├── de-DE.ini
│ │ ├── en-US.ini
│ │ ├── ru-RU.ini
│ │ └── zh-CN.ini
│ └── res
│ ├── cascadeClassifiers
│ │ ├── haarcascade_eye.xml
│ │ ├── haarcascade_eye_tree_eyeglasses.xml
│ │ ├── haarcascade_frontalface_alt.xml
│ │ ├── haarcascade_frontalface_alt2.xml
│ │ ├── haarcascade_frontalface_alt_tree.xml
│ │ ├── haarcascade_frontalface_default.xml
│ │ ├── haarcascade_fullbody.xml
│ │ ├── haarcascade_lefteye_2splits.xml
│ │ ├── haarcascade_lowerbody.xml
│ │ ├── haarcascade_profileface.xml
│ │ ├── haarcascade_righteye_2splits.xml
│ │ └── haarcascade_upperbody.xml
│ └── time.svg
└── Scripts
Now, rather than require admin privileges, I can just copy the plugin into the plugins folder:
mkdir -p ~/Library/Application\ Support/obs-studio/plugins/
rsync -avhP \
/tmp/SceneSwitcher/Payload/Library/Application\ Support/obs-studio/plugins/advanced-scene-switcher/ \
~/Library/Application\ Support/obs-studio/plugins/advanced-scene-switcher/
By AJ ONeal
Did I make your day?
Buy me a coffee
(you can learn about the bigger picture I'm working towards on my patreon page )