launchctl plist has a stderr that talks about how getcwd operation not permitted so how do I fix it?

Description


TLDR for those who googled and found this

In Catalina, launchd can't read files or run scripts located inside your ~/Documents folder. Just avoid putting your scripts there and rewrite your scripts not to read files in ~/Documents. Creating a new file or symlink will work, though. See the answer below for details.


Original question

I came via this answer to help me create a script that will update a symlink every day.

And there was an article it links to teaching me how to use plist and launchctl

SO this is my plist com.journal.today.plist which is installed in ~/Library/LaunchAgents

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>

    <key>Label</key>
    <string>com.journal.today.plist</string>

    <key>RunAtLoad</key>
    <true/>

    <key>StandardErrorPath</key>
    <string>/Users/kim/journals/stderr.log</string>

    <key>StandardOutPath</key>
    <string>/Users/kim/journals/stdout.log</string>

    <key>EnvironmentVariables</key>
    <dict>
      <key>PATH</key>
      <string><![CDATA[/usr/local/bin:/usr/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin]]></string>
    </dict>

    <key>WorkingDirectory</key>
    <string>/Users/kim/journals</string>

    <key>ProgramArguments</key>
    <array>
      <string>./makeTodaySym.sh</string>
    </array>

  </dict>
</plist>

When i check the stderr I saw the below

shell-init: error retrieving current directory: getcwd: cannot access parent
  1 bash: ./makeTodaySym.sh: Operation not permitted

stdout.log was empty

How do I solve this?


My original script

#!/usr/bin/env bash

date=`date +"%Y-%m-%d %A"`
folder="." # replace with full path to desired folder
file="$folder/$date.md"

if [ ! -f "$file" ]; then
    touch "$file"
    echo "Created file: $file"
fi

ln -sf "$file" "$folder/today.md"
echo "Created link to file: $file"

After trying out jksoegaard answer, the last part of my plist looks like this:

<key>ProgramArguments</key>
<array>
  <string>/Users/kim/journals/makeTodaySym.sh</string>
</array>

Latest change after taking jaume comment

<key>ProgramArguments</key>
<array>
  <string>/bin/bash</string>
  <string>/Users/kim/journals/makeTodaySym.sh</string>
</array>

Latest change

in the bash script

folder="$1" # replace with full path to desired folder

in the plist

<key>ProgramArguments</key>
<array>
  <string>/bin/bash</string>
  <string>/Users/kim/journals/makeTodaySym.sh /Users/kim/journals</string>
</array>

I have also tried

<key>ProgramArguments</key>
<array>
  <string>/bin/bash</string>
  <string>/Users/kim/journals/makeTodaySym.sh</string>
  <string>/Users/kim/journals</string>
</array>

And now I get this

shell-init: error retrieving current directory: getcwd: cannot access parent directories: Operation not permitted
/bin/bash: /Users/kim/journals/makeTodaySym.sh: Operation not permitted

thanks to @jaume with great patience, he/she/they have helped me get to the point where I realized that the only thing that worked was to move away from the Documents folder in Catalina.

I did find this link https://discussions.apple.com/thread/250719819?answerId=251683647022#251683647022 suggesting to create a fake bash clone, but I didn't try.

Also a big thank you to @nohillside

One useful thing I learn is to load and unload plist without restarting laptop

first unload

launchctl unload ~/Library/LaunchAgents/com.test.today.plist

then load

launchctl load -w ~/Library/LaunchAgents/com.test.today.plist


Solution


Analysis

After a long chat session, it turns out that the root of the problem was that bash can't fully access the Documents folder. The OP had shortened the actual path for makeTodaySym.sh: instead of being located in /Users/kim/journals, the script resides in a subfolder of ~/Documents.

The Documents folder is sandboxed, as explained in this Apple document:

In macOS, data in critical areas is itself sandboxed — which ensures that users remain in control of access to files in Desktop, Documents, Downloads and other areas from all apps, whether the apps attempting access are themselves sandboxed or not.

and bash can't read its contents from within the .plist file.

Two solutions

Place the script outside of ~/Documents

Interestingly enough, although bash can't read the contents of the Documents folder, it can write to it.

So one solution is to move makeTodaySym.sh outside of ~/Documents. For instance, if placed in /Users/kim/bin/makeTodaySym.sh, this should work:

<key>ProgramArguments</key>
<array>
  <string>/bin/bash</string>
  <string>-c</string>
  <string>/Users/kim/bin/makeTodaySym.sh /Users/kim/Documents/Apps/CompanyLevelApps/OILD/16-journals</string> 

(Tested on macOS 10.15.5 Catalina.)

Grant full disk access to bash

Another solution is give bash full disk access. Just add /bin/bash to System Preferences>Security & Privacy>Privacy>Full Disk Access.

Note that even with full disk access, permissions would prevent bash from reading arbitrary files in the file system, but you have one fewer level of protection.

(Tested on macOS 10.15.5 Catalina.)

Two suggestions

I'd suggest that you do the following two changes to your configuration:

Remove reference to working directory

I've noticed that setting a working directory in the .plist file:

<key>WorkingDirectory</key> 
    <string>/Users/kim/Documents/Apps/CompanyLevelApps/OILD/16-journals</string> 

causes this error:

job-working-directory: error retrieving current directory: getcwd: cannot access parent directories: Operation not permitted

Your script doesn't need the WorkingDirectory key to successfully create the symlink, so you may want to remove it to get rid of the error.

Create symlink with relative path

Your script creates the symlink:

ln -sf "$file" "$folder/today.md"

with an absolute path:

[email protected] -> /Users/kim/Documents/Apps/CompanyLevelApps/OILD/16-journals/2020-06-10 Wednesday.md

To may want to use:

ln -sf "$(basename "$file")" "$folder/today.md"

instead to create a relative symlink that is easier to read:

[email protected] -> 2020-06-10 Wednesday.md

Full path in plist file

It's important to note that providing the full path for makeTodaySym.sh (as explained in jksoegaard's answer) was a necessary change for the .plist to work properly, as relative paths are not supported.