Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detect whether macOS app was launched from the command line (Terminal)

I have a GUI macOS app that may also be launched from Terminal, with optional command line arguments.

When launched with arguments, I like to run the app in a "cmdline" mode where I do not display any UI but instead communicate via stdin + stdout only.

I can detect this cmdline mode like this:

BOOL cmdMode = NSProcessInfo.processInfo.arguments.count > 1;

(arg 0 is always the executable's path, so any more args would be manually passed args).

Now, here's the big question:

If the user invokes my app without arguments from Terminal (by invoking the app's executable in Contents/MacOS, i.e. not via the open cmd), I like to also go into the cmdline mode. How do I detect this?

Note: Older OS X versions did pass a "-psn ..." argument that, when not present, could be used to detect a launch from cmdline, but recent macOS versions seem to not pass this argument any more when launching apps from the Finder, so I cannot use that for detection any more.

Update

I realize that I can almost correctly solve this by checking for the presence of certain environment variables:

TERM and PWD are only set when launching the app from Terminal but not from Finder.

However, I also like to be able to tell the difference between being launched directly (executable in Contents/MacOS dir) vs. launched with the open command as I consider the open cmd being equivalent to opening the app via Finder or from another app via Launch Services.

In short, the question might also be: Detect whether an app was launched by Launch Services


For the record, here are the values from environ(). The ones marked with an asterisk are only present when invoked from Terminal.app but not when lanched from Finder:

    __CF_USER_TEXT_ENCODING=0x1F5:0x0:0x0
*   _=/Applications/Myapp.app/Contents/MacOS/Myapp
    Apple_PubSub_Socket_Render=/private/tmp/com.apple.launchd.laVQnD7IXl/Render
    HOME=/Users/username
*   LANG=en_US.UTF-8
*   LC_ALL=en_US.UTF-8
*   LC_CTYPE=UTF-8
    LOGNAME=username
    PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
*   PWD=/Users/username
    SHELL=/bin/bash
*   SHLVL=1
    SSH_AUTH_SOCK=/private/tmp/com.apple.launchd.KeHv8KNuuk/Listeners
*   TERM_PROGRAM_VERSION=388.1.2
*   TERM_PROGRAM=Apple_Terminal
*   TERM_SESSION_ID=EF2C59E6-D661-45BE-B7EF-7A0E71158C8D
*   TERM=xterm-color
    TMPDIR=/var/folders/hm/ycnxcbwx8xl1v7008k8wnpjh0000gn/T/
    USER=username
    XPC_FLAGS=0x0
    XPC_SERVICE_NAME=0

There are, however, no envinment values that are unique to apps launched with Launch Services (such as when double clicked in Finder).

like image 858
Thomas Tempelmann Avatar asked Oct 07 '18 10:10

Thomas Tempelmann


1 Answers

If you want to know what process has executed your program, you could use getppid() to get the parent process ID, then inspect that process to determine whether you were executed by an interactive shell process, or Finder, or launchctl, etc.

/sbin/launchd is PID 1 - if your process's parent PID is 1, you were executed by launchd.

Otherwise, you were executed by another process - probably an interactive shell, or as a subprocess of another process. You can use the KERN_PROCARGS syscall with sysctl() to get the process name by its PID.

You might also want to consider using isatty(STDIN) as well: interactive shells have a TTY, non-interactive shells and other processes won't.

like image 109
dossy Avatar answered Sep 19 '22 13:09

dossy