Manage "Open using Rosetta" in lab environment

jho
New Contributor

I have a room of M1 Mac Minis used for a Digital Art/Printing lab. The instructors like to use a plug-in that has not yet been updated to run natively on Apple silicon, so to use the plugin the students/users have been finding the Adobe Photoshop.app, getting info, and ticking the Rosetta box manually... nearly every time they go to run the app (the user account is removed upon log out and refreshed via script).

I'm curious if anyone has any experience managing these settings. They appear to be set in a plist file in the user's Library folder: ~/Library/Preferences/com.apple.LaunchServices/com.apple.LaunchServices.plist; but I have not had any success trying to manage this with a custom config profile in Jamf, nor does dropping the preconfigured file into place during account creation keep that box checked.

4 REPLIES 4

claudio_provini
New Contributor III

Hi all,

i've the same problem, i need to open with rosetta word for a plugin, but i didn't find any solution.

I've tried to put .plist file in the right directory and also package word.app with setted the flag but doesn't worked

There is someone that solved this problem?

Thanks

Claudio

claudio_provini
New Contributor III

anyone solved?

Just got done working through this so I'll post in case anyone finds it helpful.

I stumbled across this: https://gist.github.com/WardsParadox/d6a8fbe0c0d1f100b5574188e4bd627d

(Script called run_under_rosetta.py)

First off, you will need to push a Python 3 interpreter to run this script on an endpoint: https://github.com/macadmins/python

I copied the contents of run_under_rosetta.py into a new script in Jamf.

I added the appropriate shebang so the above Python interpreter would see it.

For some reason it was erroring until I added a colon on line 120 after (app_path)

I then added the path to Photoshop 2021. I usually drag an app to terminal and copy the path there. But this script didn't like the slashes that terminal was adding, so I had to remove them.

So this is the working script:

#!/Library/ManagedFrameworks/Python/Python3.framework/Versions/Current/bin/python3

import os
import subprocess
import sys

from Foundation import *  # pylint: disable=E0611 #
import objc
from SystemConfiguration import SCDynamicStoreCopyConsoleUser


def is_arm64():
    """
    Determine if a Mac can run ARM64 code, whether or not the binary is running in Rosetta 2 via pyobjc
    https://gist.github.com/pudquick/ca7fa134895f30b070212ea505cab5eb
    Returns:
        boolean: True = arm64 device (aka Apple Silicon), False = x86_64 (Intel Silicon)
    """
    CF = NSBundle.bundleWithPath_("/System/Library/Frameworks/CoreFoundation.framework")
    f = [("CFBundleIsArchitectureLoadable", b"BQ")]
    objc.loadBundleFunctions(CF, globals(), f)
    NSBundleExecutableArchitectureARM64 = 0x0100000C
    return CFBundleIsArchitectureLoadable(NSBundleExecutableArchitectureARM64)


# Shamelessly Stolen from FoundationPlist (L26-106): https://github.com/munki/munki/blob/main/code/client/munkilib/FoundationPlist.py
class NSPropertyListSerializationException(BaseException):
    """Read/parse error for plists"""

    pass


class NSPropertyListWriteException(BaseException):
    """Write error for plists"""

    pass


def readPlist(filepath):
    """
    Read a .plist file from filepath.  Return the unpacked root object
    (which is usually a dictionary).
    """
    plistData = NSData.dataWithContentsOfFile_(filepath)
    (
        dataObject,
        dummy_plistFormat,
        error,
    ) = NSPropertyListSerialization.propertyListFromData_mutabilityOption_format_errorDescription_(
        plistData, NSPropertyListMutableContainers, None, None
    )
    if dataObject is None:
        if error:
            error = error.encode("ascii", "ignore")
        else:
            error = "Unknown error"
        errmsg = "%s in file %s" % (error, filepath)
        raise NSPropertyListSerializationException(errmsg)
    else:
        return dataObject


def readPlistFromString(data):
    """Read a plist data from a (byte)string. Return the root object."""
    plistData = NSData.dataWithBytes_length_(data, len(data))
    if not plistData:
        raise NSPropertyListSerializationException("Could not convert string to NSData")
    (
        dataObject,
        dummy_plistFormat,
        error,
    ) = NSPropertyListSerialization.propertyListFromData_mutabilityOption_format_errorDescription_(
        plistData, NSPropertyListMutableContainers, None, None
    )
    if dataObject is None:
        if error:
            error = error.encode("ascii", "ignore")
        else:
            error = "Unknown error"
        raise NSPropertyListSerializationException(error)
    else:
        return dataObject


def writePlist(dataObject, filepath):
    """
    Write 'rootObject' as a plist to filepath.
    """
    (
        plistData,
        error,
    ) = NSPropertyListSerialization.dataFromPropertyList_format_errorDescription_(
        dataObject, NSPropertyListXMLFormat_v1_0, None
    )
    if plistData is None:
        if error:
            error = error.encode("ascii", "ignore")
        else:
            error = "Unknown error"
        raise NSPropertyListSerializationException(error)
    else:
        if plistData.writeToFile_atomically_(filepath, True):
            return
        else:
            raise NSPropertyListWriteException(
                "Failed to write plist data to %s" % filepath
            )


def main():
    """Main, duh, whatelse but RUNNNN!"""
    if not is_arm64():
        print("Intel device detected...no problemo!")
        sys.exit(0)
    # Dont' rely on python knowing which user is active as we run this run sudo via jamf
    name, uid, gid = SCDynamicStoreCopyConsoleUser(None, None, None)
    ls_path = f"/Users/{name}/Library/Preferences/com.apple.LaunchServices/com.apple.LaunchServices.plist"
    ls_mode = "x86_64"
    app_path = "/Applications/Adobe Photoshop 2021/Adobe Photoshop 2021.app"
    if not os.path.exists(app_path):
        print(f"Bad App Path given!")
        sys.exit(1)
    bundle_identifier = NSBundle.bundleWithPath_(app_path).bundleIdentifier()
    file_path = NSURL.fileURLWithPath_(app_path)
    bookmark = file_path.bookmarkDataWithOptions_includingResourceValuesForKeys_relativeToURL_error_(
        NSURLBookmarkCreationSuitableForBookmarkFile, None, None, None
    )
    data = NSData.alloc().initWithBytes_length_(bookmark[0], len(bookmark[0]))

    if os.path.exists(ls_path):
        ls_data = readPlist(ls_path)
    else:
        dummy = plistlib.dumps({"Architectures for arm64": {}})
        ls_data = readPlistFromString(dummy)

    if bundle_identifier in ls_data["Architectures for arm64"]:
        if ls_mode in ls_data["Architectures for arm64"][bundle_identifier]:
            print(f"App bundle {bundle_identifier} already set to {ls_mode}")
            sys.exit(0)
        else:
            print(f"Setting App bundle {bundle_identifier} to {ls_mode}")
    else:
        print(
            f"App bundle {bundle_identifier} not found. Creating and Setting to {ls_mode}"
        )

    ls_data["Architectures for arm64"][bundle_identifier] = [data, ls_mode]
    writePlist(ls_data, ls_path)
    # Kill LaunchServices daemon to load saved data
    subprocess.check_call(["/usr/bin/killall", "lsd"])


if __name__ == "__main__":
    main()

 

In my use-case, a companies (GMetrix) photoshop plug-in wouldn't work without Photoshop running under Rosetta. It's a shared lab, and I didn't want every student having to check the 'Open using Rosetta' box.

I have the GMetrix install.pkg policy running a recon after if finishes. I also have a smart group called 'Has GMetrix installed'. I have the above script/policy run on a Login hook, targeting the 'GMetrix installed' smart group. So anyone with that app (well, it shows as an installed .app, but it's essentially just a Photoshop plug-in) installed, this script will run when they login. There is a ~5 second delay until the Rosetta box gets checked for Photoshop, but it works. Tested with the latest version of Monterey, but it should work with Ventura I think.

 

 

whiteb
Contributor II

Update: for some reason, this only seems to work for Photoshop 2021 and not 2022. Not sure why.