Squirrel.Mac
Squirrel.Mac copied to clipboard
The file "ShipItState.plist" couldn’t be opened because there is no such file
In an Electron-based app, I encountered an issue where one user was unable to update. The logs in ShipIt_stderr.log
showed this message over and over:
Installation error: Error Domain=SQRLShipItRequestErrorDomain Code=2 "Could not read update request" UserInfo=0x7fe4cae09930 {NSLocalizedDescription=Could not read update request, NSUnderlyingError=0x7fe4cae07d50 "The file “ShipItState.plist” couldn’t be opened because there is no such file."}
However, ShipItState.plist
did exist, but the two log files had different permissions than the plist
file and the update directory:
drwxr-xr-x 7 foo staff 238B Sep 9 17:16 .
drwx------+ 41 foo staff 1.4K Sep 9 17:16 ..
-rw-r--r--@ 1 foo staff 6.0K Sep 9 17:24 .DS_Store
-rw-r--r-- 1 foo staff 262B Sep 9 17:16 ShipItState.plis
-rw-r--r-- 1 root staff 195K Sep 9 16:55 ShipIt_stderr.log
-rw-r--r-- 1 root staff 0B Sep 9 16:38 ShipIt_stdout.log
drwx------ 4 foo staff 136B Sep 9 17:24 update.c3etqCW
Doing a chown
fixed the issue:
sudo chown foo:staff ShipIt_stderr.log ShipIt_stdout.log
It looks like others are hitting this same issue: https://github.com/atom/atom/issues/2860#issuecomment-121111312
It looks like SQRLUpdater.m
has some logic around running as root/non-root, but if the log files are owned by root and being updated then I would assume it's running as root and should be able to read the ShipItState.plist
file that's owned by a non-privileged user:
NSError *targetWritableError = nil;
BOOL gotWritable = [targetURL getResourceValue:&targetWritable forKey:NSURLIsWritableKey error:&targetWritableError];
// If we can't determine whether it can be written, assume nonprivileged and
// wait for another, more canonical error.
return [SQRLShipItLauncher launchPrivileged:(gotWritable && !targetWritable.boolValue)];
Another example of the same issue: https://discuss.atom.io/t/update-not-working-com-github-atom-shipit-respawns-all-the-time/9819/15
Good debugging, does creating these log files ahead of time as the non-priviledged user fix this? Can we get an improved error message in this failure case given that it’s the log file permissions at fault and not the non existence of the install request file?
I'll see if I can reproduce it on the machine that had the issue and try pre-creating the log files.
Unfortunately, I haven't been able to reproduce the issue, so it seems like it must be a race. Perhaps the best thing is to intentionally chown
the files as shown above and see if squirrel can be made to be defensive about the situation?
Reproducing this error is rather easy. Just chmod the ~/Library/ with 700 as it is supposed to be. At least that is what it used to be way back. [Edit: the folder!, not everything contained within and definitely not all files]
Why? Because your actual User/Name folder is 755, so in a multiuser Account people can come in and see your folders. From there, /Users/myName/ , they then can access your public folder where you can share stuff with other users of that computer. It also contains the dropbox were they can pop files in (not the one from the application, the one that every OSX users has).
Anyway, all this works because /Users/myName/ is 775 and if you want to stop people looking in your folders below you have to set them to non executable for "group" and "everyone", typically that is 700.
This means that you need to be either the owner or someone who can overwrite the owners privileges to go there. Most applications ask for a password when they install themselves for the first time. Then they create a folder inside which can have less restrictive i.e. 775 permissions. In this folder they can than write without asking every bloody time*. Because permissions are not inherited. They are local. So you can have them nested. Which is sort of the concept of the ~/ folder anyway since it is also nested inside root.
Needless to say I found this topic by wondering how I can stop this raging menace. I need the syslog and rebooting would obviously be pointless.
*and not actually saying what that "helper application" is. I update atom only manually anymore. It just won't say what it is going todo but it wants to be potentially root.
Was there ever a permanent fix to this issue? What can I do with the computer novice out in the wild that cannot update and gets this prompt all the time?
Getting this error now, with Atom. ~/Library is mode 700 for me (using El Capitan).
This issue appears to happen when people open the app directly from the DMG instead of copying to their Applications folder first.
The app will open fine, but once the download finishes updating the following prompt appears:
When the password is entered, Squirrel is now run as root
, the log files are created as root
, and the update process no longer works.
Squirrel will now dump the Installation error log mentioned above into the Mac Console, as well as continue to try every second and keep dumping it in the ShipIt_stderr.log
file. Over time, that file grows without bound. By the time I had discovered it, the log file was 500MB!
To make matters worse, this is not fixed by "reinstalling" the app. If a user deletes the app from their Applications folder and puts a new one in its place, the ~/Application Support/com.your.name.ShipIt
folder is not deleted. The root state is not fixed.
This seems to be related to https://github.com/Squirrel/Squirrel.Mac/issues/163
Any ideas on how to fix this? (Other than telling our users to rm their Application Support folder and remove the launch agents!)
Unfortunately, this means some percentage of our users (those that launched the app via the DMG) are likely permanently stuck on old versions!
Based on our update logs. I estimate this is affecting about 3-6% of our entire user base.
We stopped distributing the dmg and instead distribute the .app file which seems to work fine, but doesn't give the user a nice interface to install to the apps directory.
Our hacky but seemingly effective solution to this is to (ASAP) check the app.getPath('exe')
path to see if it's mounted in "/Volumes" and if it is, exit immediately. This at least prevents it from being run out of the DMG.
Something like this:
if(app.getPath('exe').slice(0,8) === "/Volumes") {
dialog.showErrorBox("App cannot be run directly from DMG", "This app cannot be run directly from the DMG. Please install it to 'Applications' before trying to run it.");
app.exit(0);
}
Chiming in here to mention a possible situation that causes this: our IT team has software that runs to automatically "correct" permissions and apply updates, and somewhere in that it mixed up the permissions for the Visual Studio Code.app (which uses Squirrel via Electron). In my case I had sufficient permissions to manually chown'ing the .app file back to myself.
I have a 100% repro of this. You don't need to modify permission, as most users wouldn't, I assume. Here's the scenario:
- A user with admin access installs an Electron app in /Applications.
- Then a standard user (with no admin access by default) will see the app in his Applications folder.
- The standard user launches the app and performs an update, at which point he will see the admin prompt @emorikawa posted above.
- Knowing the admin credentials, the standard user enters them and proceeds with the update.
- the autoUpdater in Electron doesn't think it failed, but Squirrel.Mac ends up failing.
I was able to get more specific errors than the error OP saw.
2017-03-10 14:34:50.854 ShipIt[32437:120411] Installation error: Error Domain=SQRLShipItRequestErrorDomain Code=2 "Could not read update request" UserInfo={NSLocalizedDescription=Could not read update request, NSUnderlyingError=0x7fdfa9d10460 {Error Domain=NSCocoaErrorDomain Code=260 "The file “ShipItState.plist” couldn’t be opened because there is no such file." UserInfo={NSFilePath=/var/root/Library/Caches/com.app.app1.ShipIt/ShipItState.plist, NSUnderlyingError=0x7fdfa9d0de90 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}}} 2017-03-10 14:34:50.854 ShipIt[32437:120411] ShipIt quitting
It looks like Squirrel.Mac looks for the ShipItState.plist in /var/root/Library/Caches/com.app.app1.ShipIt instead of /Users/user/Library/Caches/com.app.app1.ShipIt, perhaps due to the elevated privilege when the standard user entered the admin credentials?
I think this could be a very common case as there are many users with admin access who use a standard user account on purpose.
Do we have any fix or workaround?
Only workaround is to distribute as a .app file instead of dmg. Other than that you might have to become a contributor? This is a major bug and has been around for 1.5 years.
@btelintelo - thanks for the suggestion. Even if an .app file is given instead of dmg, if the admin user installs it in /Applications, then other standard users will still run into the same issue I think. I wish I knew enough objective-c and Cocoa :P
I still see this today. Any updates? Any tips? What is the best way to distribute then? This is pretty critical flow, I can see a lot of people opening the app even if by mistake.
Same here, I'm trying to get rid of ShipIt altogether, it's insane. On 10.12.6, running 1.19.4. I can't find what keeps launching ShipIt, it's not called *shpit*, nor *squirrel*, I have no idea how to find this thing! Nothing jumps at me in terms of launchctl, there's no LaunchAgents, no LaunchDaemons, this is some ghostbusters stuff.
I'm running into a similar/related issue, with one difference being I have a pkg installer that dumps the .app
file to /Applications
. The error I see is:
2017-10-01 15:45:54.015 ShipIt[22296:2926535] Installation error: Error Domain=SQRLShipItRequestErrorDomain Code=2 "Could not read update request" UserInfo={NSLocalizedDescription=Could not read update request, NSUnderlyingError=0x7fdd01611f10 {Error Domain=NSCocoaErrorDomain Code=260 "The file “ShipItState.plist” couldn’t be opened because there is no such file." UserInfo={NSFilePath=/var/root/Library/Caches/com.electron.fenix.ShipIt/ShipItState.plist, NSUnderlyingError=0x7fdd01611a20 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}}}
2017-10-01 15:45:54.015 ShipIt[22296:2926535] ShipIt quitting
The main point of interest is in the first message, where it chokes on /var/root/Library/Caches/....
, which truly does not exist. The file actually exists at ~/Library/Caches/....
.
FWIW, I'm building on High Sierra.
Major bug, project doesn't have any maintainers.
@coreybutler That sounds like https://github.com/Squirrel/Squirrel.Mac/issues/131 which was fixed in https://github.com/Squirrel/Squirrel.Mac/pull/207.
Thanks @joshaber - Did #207 just land in Electron? I had this issue in 1.7.6. I can't recreate the issue in 1.7.8.
Yes, it’s in Electron though I’m not sure which versions include the fix.
@coreybutler It's still happening to me in 1.7.8 --- did you clear out any specific directories before getting it to work?
I've cleared the following:
rm -rf /Applications/MyApp.app
pkgutil --forget com.mycompany.myapp
rm -rf ~/Library/Application\ Support/MyApp
rm -rf ~/Library/Caches/com.mycompany.myapp
rm -rf ~/Library/Caches/com.mycompany.myapp.ShipIt
And when I rebuild using electron-builder and "electronVersion": "1.7.8"
, then install the .pkg file as an admin user from my Downloads folder, I still get:
Installation error: Error Domain=SQRLShipItRequestErrorDomain Code=2 "Could not read update request" UserInfo={NSLocalizedDescription=Could not read update request, NSUnderlyingError=0x7ff736e00790 {Error Domain=NSCocoaErrorDomain Code=260 "The file “ShipItState.plist” couldn’t be opened because there is no such file." UserInfo={NSFilePath=/var/root/Library/Caches/com.mycompany.myapp.ShipIt/ShipItState.plist, NSUnderlyingError=0x7ff736e00cc0 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}}}
Anything I might be missing in clearing out old directories?
@etyp - I removed the caches the same way you did, but that was the only cleanup effort I attempted between testing Electron 1.7.6 and 1.7.8. However; a major difference in my environment... I'm not using electron-builder. I don't know if there is anything in electron-builder that could be causing this, and I didn't check since my initial issue manifested via squirrel.mac.
If it's any help, my build process (a gulp task) for macOS is:
- Package via electron-packager.
- Make minor modifications to resulting file resources/permissions (specifically to correct https://github.com/electron/electron/issues/10634).
- Code sign the app via electron-osx-sign.
-
pkgbuild
via child process to build a.pkg
installer. -
productsign/productbuild
via child process to sign the installer. - Compress the
.app
via 7zip-bin (same module used internally by electron-builder) - the resulting zip file is what I ultimately serve to Electron autoUpdater (again, not using electron-builder's custom autoUpdate).
Steps 4 & 5 are specific to my installer (not updates), so it shouldn't have any impact on this issue. I'm using Electron 1.7.8 now and don't enforce any kind of build caching in the aforementioned process. It's a painfully slow build, but works flawlessly every single time.
@coreybutler thanks - followed your process all the way through (except I wasn't sure exactly what to do with step # 2, so if that's critical here please let me know specifics around what needs to be changed).
Unfortunately, I was still prompted to install the helper with admin info and got the same issue once the helper was installed:
Installation error: Error Domain=SQRLShipItRequestErrorDomain Code=2 "Could not read update request" UserInfo={NSLocalizedDescription=Could not read update request, NSUnderlyingError=0x7ffb6250caa0 {Error Domain=NSCocoaErrorDomain Code=260 "The file “ShipItState.plist” couldn’t be opened because there is no such file." UserInfo={NSFilePath=/var/root/Library/Caches/com.electron.myapp.ShipIt/ShipItState.plist, NSUnderlyingError=0x7ffb62502d00 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}}}
Confirmed that I'm definitely on Electron 1.7.8 - not quite sure what to try next and will keep trying. Let me know if step # 2 was critical to this working for you.
@etyp - I don't think step 2 is critical, because that issue has been a part of Electron for a long time. However; if you want to try it, my code is basically a two-liner for that step:
let electronPath = path.join(appPath, 'Contents', 'Frameworks', 'Electron Framework.framework')
exec(`chmod +x "${path.join(electronPath, 'Versions', 'A', 'Libraries', 'libffmpeg.dylib')}"`)
exec
is an alias of child_process.execSync and appPath
is the absolute path of the .app
archive generated by electron-packager.
I honestly don't think this will change your results, but I'm out of ideas.
It looks like the fix was released in Electron 1.7.9: https://github.com/electron/electron/releases/tag/v1.7.9
@joshaber hmm, yeah looks like it includes the bump to v1.2.1 PR from electron/electron#10071, but I now get the issue mentioned on #212 as a result with Electron 1.7.9
Any idea how I can best track when https://github.com/electron/electron/pull/10298/files will be added to an Electron build version?
Thanks :)
-- Update: I tried it with Electron 1.8.1 which seems to have binary versions at 1.2.2 and still have the same issue. Not sure if the binary versions fix this or maybe I need to clear out some local cache when building?
@etyp I downloaded the version tagged Electron 1.7.9 and verified it has the proper ShipIt version. I'm not sure if there could be caching at play.