How to install system-wide, share content and keep sandboxing conformity?


#1

Hi JUCErs,

My scenario:

  • I want my plugins to share a large-size content, so it’s not copied in each bundle (on Mac);
  • Sandboxed hosts allow me to read only 1 dir, and what is worse only on user level ‘~/Music’.

The issue:

  • Since AAX can only be installed on system level, I am installing all plugins at system level. But the sandboxing-ready AU would need a copy of all resources in the current user’s ~/Music/[etc.].

Is there a way to have only 1 shared copy of these resources for all plugins, have them installed system-wide and remain sandboxing-complient?

P.S.

  • I know about the workaround to read the entire disk normally, but that seems hacky and seems very probably to get “fixed” even in a minor OS/DAW update, so I am not willing to take this route. So while I am grateful for any info you may want to share, I would like to stick to officially acceptable (by Apple) solutions. Thanks for the understanding.

Where do you keep shared read-only and user content?
#2

I currently see this option to be far away from optimal user experience:

  • in anything sandboxed (plugin or app) you ask the user to select the path to the plugin resources (the PowerBox component), which should enable permanently that location as an access.

Another option seems more reasonable to me - File Access Temporary Exceptions.
I read from an unofficial source (stackoverflow) that “Temporary” here refers to this option’s uncertain future (it can be removed in a future OS X version?). Anyone using that and/or having an opinion?


#3

I would consider the option to use hardlinks, i.e. the two file entries in the two different directories would share the same physical data on disk.

I have never done that myself, just giving a pointer for you to have a thought about it.


#4

Thanks for the reply. I’ve considered that.

I don’t think I am going to create hard links. These are my reasons:

  • Can’t assume this is allowed (I think I am safe at considering it’s not, actually), since there doesn’t even seem to be a cli tool for hard link creation, provided with the OS (ln on OS X can’t do it) and I don’t see any opinion on the matter from Apple - it’s almost like a radio-silence (or static :slight_smile: ) on a topic they don’t want discussed (hard links and sandboxing);
  • I know I can’t prevent users from messing up their installation (unless everything is embedded and that’s not an option), but deleting a hard link deletes the original content it’s linked to (and all other hard links), because it basically is the same representation of the original content.

Overall, this seems too low-level as a solution (to mess with sandboxing on file system level) and until I read a statement from Apple that says otherwise I consider it a hack to overcome Sandboxing.

Still I am thankful for the suggestion. Cheers!


#5

Does APFS still support hard links?


#6

Good point!

Does Apple File System support directory hard links?

Directory hard links are not supported by Apple File System. All directory hard links are converted to symbolic links or aliases when you convert from HFS+ to APFS volume formats on macOS.


#7

I use the “~/Music/Audio Music Apps/ManufactorName/mystuff…” approach, which of cause isn’t ideal because its user specific, but i don’t know a better solution.

BTW:
If you are going this route, and you installer creates the “Audio Music Apps” folder, be sure it has “username” as owner. (There were installer of a specific company who did this wrong)


#8

Thanks, I’ll keep that in mind. Right now I think I am going to take the entitlements route (if there aren’t any other issues I can’t currently see).


#9

Well, I was talking about hardlinks to files, not whole directories.


#10

There doesn’t seem to be the ability to add entitlements to a plugin (AU) target… And I am guessing this is in general the case. It will suck if I have to go around the Sandboxing and hack it, but if there is no other solution, I can’t have those resources copied in all targets (or even user directories) and I want to share them in a single central place.

I am hoping someone from the JUCE team may be able to comment.


#11

@yfede I saw your approach with getting the user directory with this:

#include <pwd.h>

File getRealUserHomeDirectory ()
{ 
    struct passwd *pw = getpwuid (getuid ());
    if (pw == nullptr)
    {
        jassertfalse;   // unable to read the user info
        return File::getSpecialLocation (File::userHomeDirectory);
    }

    return File (String (pw->pw_dir));
}

Do you always install on user level? Or maybe you use this only for user-written data (not default content shared between plugins)?

Because there is no user-level installation for AAX, I thought I should always install on system level, so shared resources should also be on system level… but then even this trick doesn’t do it, does it?

The only thing I can think of now, would be to have the path hardcoded ("/Library/Application Support/[company]/[app]") and hope it is available on every OS X version since 10.8… Actually I do assume this from my installer already, so I guess this will be it. Will test it.


#13

All of our plug-ins have their data files located at the following paths (on Mac):

  1. shared, read-only: /Library/Application Support/[Manufacturer]/[Product]

    This is obtained in JUCE with the following:

    return
    #if JUCE_MAC
        File ("/Library/Application Support")
    #elif JUCE_WINDOWS
        File::getSpecialLocation (File::commonApplicationDataDirectory)
    #else
    #error Unsupported platform
    #endif
        .getChildFile (manufacturer).getChildFile (product);
    
  2. shared, read/write: /Users/Shared/[Manufacturer]/[Product]

    This is obtained in JUCE with the following:

        return File::getSpecialLocation (File::commonDocumentsDirectory).getChildFile (manufacturer).getChildFile (product);
    

    NB: In hindsight, for consistency with the others I would have preferred it to be:
    /Users/Shared/Library/Application Support/[Manufacturer]/[Product]

    coded like:

    return
        File::getSpecialLocation (File::commonDocumentsDirectory)
    #if JUCE_MAC
        .getChildFile ("Library").getChildFile ("Application Support")
    #endif
        .getChildFile (manufacturer).getChildFile (product);
    
  3. user, read/write: ~/Library/Application Support/[Manufacturer]/[Product]

    return
    #if JUCE_MAC
        getRealUserHomeDirectory().getChildFile ("Library").getChildFile ("Application Support")
    #elif JUCE_WINDOWS
        File::getSpecialLocation (File::userApplicationDataDirectory)
    #else
    #error Unsupported platform
    #endif
        .getChildFile (manufacturer).getChildFile (product);
    

This is currently working in the following plug-ins formats:

  • AAX
  • AU (non-sandboxed)
  • RTAS (only the older ones)
  • VST
  • VST3

#14

Oh, and I forgot to mention, that the files in the shared paths are initially put there by the installer, Packages in our case.


#15

Thanks @yfede! I appreciate the detailed explanation. This comes as a good validation for me. Aside from #2 - that’s my exact strategy at the moment (including installing).


#16

#2 is only used in one product among ten or so (that’s the one that required it to be written quickly).

All the others only use #1 for stuff like graphic assets, PDF Manual, factory presets, and #3 for user data like user presets, authorization data, preferences, etc.


#17

I would have preferred to tuck it inside “Library/Application Support”

BTW, you meant "/Library/Application Support” I suppose?


#19

No, I’ve now edited my original post to make it more clear what I meant. Check the explaination for the #2 path


#20

Sorry, I am pretty tired here so maybe I am missing the point, but I just saw you wrote - AU(non-sandboxed)…
Perhaps my assumption is wrong - File::getSpecialLocation () returns only within the sandboxing container (when sandboxed), but I am able to directly open files with hardcoded paths when my AU is running in GarrageBand for example.

So I assume that File ("/Library/Application Support/[etc.]") would work in any case, including sandboxing, while File::getSpecialLocation () doesn’t work in sandboxing scenario.


#21

Yes, the AU I work with have not yet been modified in order to comply with sandboxing.

Practically speaking, at the present moment Logic does not care about sandboxing, it loads non-sandboxed AUs just fine AFAIK.

The problem is GarageBand, which shows a confirmation dialog about lowering its own security settings when you load a non-sandboxed plug-in, but if you confirm that you want to, it will comply and load the plug-in anyway.


#22

Understood. Thanks for the help, @yfede.