从Mac App Bundle访问/Library/应用程序支持的权限

您所在的位置:网站首页 mac文件所有者权限 从Mac App Bundle访问/Library/应用程序支持的权限

从Mac App Bundle访问/Library/应用程序支持的权限

2023-03-27 14:52| 来源: 网络整理| 查看: 265

百度翻译此文   有道翻译此文 问题描述

I have an app bundle (built with Unity 3d if that is relevant) that I am able to create a .pkg installer using productbuild and distribute on the App Store without issue. However, the app downloads and caches a fair amount of media and has some optional configuration files that need to be shared between all users on the machine. According to Apple's documentation the configuration files should probably go in the /Library/Application Support directory and the media in /Library/Caches. I've made a modified version of that app that uses those directories instead of the ones that the sandboxed app can access, but it doesn't have permission to the /Library directory unless I run the app as root, which isn't a realistic option.

I've searched google for several hours, but I can't seem to find anything about creating such an installer. I did read this answer which has a screenshot of an installer that has the option to install for all users, but either I'm missing some option to enable that or that screenshot is just outdated because I can't seem to create a .pkg that gives me that option.

So I guess my question boils down to this: how do I package my app so it can be installed for all users, and have permission to read and write to /Library/Application Support/{app name}, or is there another preferred way to share configuration files and/or media between multiple users on the same machine?

推荐答案

For anyone else who has a similar problem, the correct answer is that you can't do this using productbuild, but you can using pkgbuild.

My productbuild build step for the app store looks like this:

productbuild --component "{mystoreapp.app/}" /Applications --sign "{signing identity}" "{mystorepkg.pkg}"

The corresponding packing command for pkgbuild looks like this:

pkgbuild --component "{mymodifiedapp.app/}" --sign "{signing identity}" --ownership preserve --scripts "{path/to/my/scripts}" --identifier {com.yourcompany.yourapp} --version "{versionNumber}" --install-location /Applications "{mymodifiedpkg.pkg}"

Note that signing is optional here as it will be distributed outside the store. where {path/to/my/scripts} has a file called postinstall in it that looks like this:

#this function creates a directory if it doesn't exist create_directory() { if [[ ! -d "$1" ]] then if [[ ! -L "$1" ]] then echo "directory $1 doesn't exist. Creating now" mkdir "$1" echo "directory $1 created" else echo "directory $1 exists" fi fi } #this function gives all users read and write (and execute) access to the directory fix_dir_permissions() { chmod 777 "$1" } baseDirName="/Library/Application Support/{your app name bere}" subDirs[0]="$baseDirName/{sub dir here}" #create base directory create_directory "$baseDirName" #create all subdirectories and give permissions for subDir in "${subDirs[@]}" do create_directory "$subDir" fix_dir_permissions "$subDir" done exit 0

This script will run after the install is over, and will create your application support directory as well as any subdirectories you need and change the permissions on them so all users have access to them.

其他推荐答案

I solved the same problem by creating a little help app that is called from the main program at any time access to "Application Support" is needed. The helper program is spawned from the main program using AuthorizationExecuteWithPrivileges. It only calls functions to create a directory and set permissions. So all is done from within the main application, not from a installer package. AuthorizationExecuteWithPrivileges (yes its offically deprecated) then shows a macos system window that requires to enter a password etc.

For this part quite a nice implementation exists here: https://github.com/sveinbjornt/STPrivilegedTask/

It is important to:

wait until the called program is finished then wait again until the folder exists (which may need some msecs)

It took me quite a while to get this done, so i'll share it here. Although the application is C++ the code here must use obective-c. See my post here for signing and notarizing this: How to notarize an app bundle containing helpers embedded in it?

The helper program:

#import #include #include int main(int argc, const char * argv[]) { @autoreleasepool { NSString *supportDirectory = [NSString stringWithFormat:@"%s", argv[1] ]; NSError *error = nil; BOOL success = [[NSFileManager defaultManager] createDirectoryAtPath:supportDirectory withIntermediateDirectories:YES attributes:nil error:&error]; if (!success) return -1; BOOL success2 = [[NSFileManager defaultManager] setAttributes:@{ NSFilePosixPermissions : @0777 } ofItemAtPath:supportDirectory error:&error]; } return 0; }

Caller Process:

void macCheckCreateFolderAdmin(std::string path) { NSString *supportDirectory = [NSString stringWithFormat:@"%s", path.c_str() ]; NSError *error = nil; BOOL success; if ([[NSFileManager defaultManager] fileExistsAtPath: supportDirectory]) return; char *args[] = { const_cast(path.c_str()), NULL }; AuthorizationRef authorizationRef; OSStatus status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &authorizationRef); NSString *nsExecPath = NSProcessInfo.processInfo.arguments[0]; const char *applFolder = [nsExecPath UTF8String]; std::string s = std::string(applFolder); std::size_t found = s.rfind("/"); if( found != std::string::npos) s = s.substr(0, found + 1); s += "myHelperProgram"; FILE *outputFile = nullptr; OSStatus status2 = AuthorizationExecuteWithPrivileges(authorizationRef, s.c_str(), kAuthorizationFlagDefaults, args, &outputFile); if(status2 != 0) return; int processIdentifier = fcntl(fileno(outputFile), F_GETOWN, 0); pid_t pid = 0, count = 0; while(count < 30 && (pid = waitpid(processIdentifier, &status, WNOHANG)) == 0) { count ++; usleep(200 * 1000); } } }


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3