Squirrel.Mac icon indicating copy to clipboard operation
Squirrel.Mac copied to clipboard

The file "ShipItState.plist" couldn’t be opened because there is no such file

Open thughes opened this issue 8 years ago • 29 comments

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)];

thughes avatar Sep 09 '15 21:09 thughes

Another example of the same issue: https://discuss.atom.io/t/update-not-working-com-github-atom-shipit-respawns-all-the-time/9819/15

thughes avatar Sep 09 '15 22:09 thughes

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?

keithduncan avatar Sep 10 '15 05:09 keithduncan

I'll see if I can reproduce it on the machine that had the issue and try pre-creating the log files.

thughes avatar Sep 10 '15 17:09 thughes

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?

thughes avatar Sep 10 '15 21:09 thughes

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.

eject-it avatar Sep 10 '15 22:09 eject-it

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?

btelintelo avatar Feb 08 '16 19:02 btelintelo

Getting this error now, with Atom. ~/Library is mode 700 for me (using El Capitan).

perlun avatar Feb 23 '16 12:02 perlun

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:

image

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!

emorikawa avatar Mar 11 '16 15:03 emorikawa

Based on our update logs. I estimate this is affecting about 3-6% of our entire user base.

emorikawa avatar Mar 11 '16 15:03 emorikawa

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.

btelintelo avatar Mar 11 '16 15:03 btelintelo

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);
}

dknutsen avatar Jun 25 '16 17:06 dknutsen

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.

scottsb avatar Aug 09 '16 21:08 scottsb

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:

  1. A user with admin access installs an Electron app in /Applications.
  2. Then a standard user (with no admin access by default) will see the app in his Applications folder.
  3. The standard user launches the app and performs an update, at which point he will see the admin prompt @emorikawa posted above.
  4. Knowing the admin credentials, the standard user enters them and proceeds with the update.
  5. 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?

fancyydk avatar Mar 13 '17 16:03 fancyydk

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 avatar Mar 13 '17 16:03 btelintelo

@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

fancyydk avatar Mar 13 '17 16:03 fancyydk

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.

alvesl avatar Jun 26 '17 21:06 alvesl

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.

working-name avatar Aug 28 '17 22:08 working-name

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.

coreybutler avatar Oct 01 '17 20:10 coreybutler

Major bug, project doesn't have any maintainers.

btelintelo avatar Oct 02 '17 00:10 btelintelo

@coreybutler That sounds like https://github.com/Squirrel/Squirrel.Mac/issues/131 which was fixed in https://github.com/Squirrel/Squirrel.Mac/pull/207.

joshaber avatar Oct 05 '17 23:10 joshaber

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.

coreybutler avatar Oct 06 '17 00:10 coreybutler

Yes, it’s in Electron though I’m not sure which versions include the fix.

joshaber avatar Oct 06 '17 14:10 joshaber

@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 avatar Oct 13 '17 22:10 etyp

@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:

  1. Package via electron-packager.
  2. Make minor modifications to resulting file resources/permissions (specifically to correct https://github.com/electron/electron/issues/10634).
  3. Code sign the app via electron-osx-sign.
  4. pkgbuild via child process to build a .pkg installer.
  5. productsign/productbuild via child process to sign the installer.
  6. 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 avatar Oct 13 '17 23:10 coreybutler

@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 avatar Oct 14 '17 00:10 etyp

@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.

coreybutler avatar Oct 14 '17 01:10 coreybutler

It looks like the fix was released in Electron 1.7.9: https://github.com/electron/electron/releases/tag/v1.7.9

joshaber avatar Oct 16 '17 11:10 joshaber

@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 avatar Oct 16 '17 17:10 etyp

@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.

joshaber avatar Oct 16 '17 20:10 joshaber