With drush 4.5 I was able to use drush to run cron with the following entry in cron.d/drupal7
0 * * * * www-data [ -x /usr/local/bin/drush ] && /usr/local/bin/drush cron > /dev/null 2>&1
After upgrading to drush 5.0-dev (Jan 6th edition) cron no longer runs and I get the following error when I run as the apache user, www-data.
$ /usr/local/bin/drush cron
Unable to write in /var/www. Please check directory permissions. [error]
file_put_contents(/5.0-dev-commandfiles-0-eaf5e7c2621e2c8c454a5e2e36d70645.cache): failed to open stream: [warning]
Permission denied cache.inc:434
I don't see any changes in permissions and apache has never had write privileges to /var/www. The command does run, however, when run as root.
Comments
Comment #1
greg.1.anderson commentedThe problem here is with the drush commandfile cache. Drush examines various environment variable and the current $HOME directory to determine where to cache these files. The webserver does not always have $HOME defined, and the environment can similarly be minimal. If Drush could not find an appropriate place to put the cache files, it erroneously tried to put them at /cache.
I committed a fix that makes the cache folder location more robust.
Comment #2
dude4linux commentedGreg, Thanks for the quick response. I pulled the latest version from the git repository and tried again. This time I don't get the warning message, but the error persists and cron doesn't run. I checked and in my installation of apache2 on Ubuntu, $HOME for the www-data user is set to /var/www. I think drush is trying to write to $HOME/.drush Perhaps this should be a warning rather than an error.
Comment #3
greg.1.anderson commentedYou should follow the instructions in
drush topic docs-cron, which explains how to set up cron to run drush-5. If $HOME/.drush/cache is not writable, then drush should not be using that location any longer. If you are experiencing different results, please detail your experiences here, following the reporting guidelines.Comment #4
dude4linux commentedHere are the results when I follow the instructions in drush topic docs-cron:
It works for drush 4.5 but not for 5.0-dev.
FWIW all drush 5.0-dev commands (not just cron) fail with the same error when run as the apache user www-data.
Comment #5
greg.1.anderson commentedI was relying on drush_mkdir to determine if the location is writable, but drush_mkdir calls drush_set_error.
Comment #6
greg.1.anderson commentedI committed yet another improvement to the cache directory selection code that more robustly handles situations where a cache directory that drush would like to use exists, but is unwritable.
If this code does not work in your environment, then try adding
CACHE_PREFIX=/path/to/cachein your list of environment variables when you call cron, and make sure that it specifies a cache directory that the www-data user can write to.Comment #7
dude4linux commentedHi Greg, Sorry to be such a PITA, but it's still not working for me. I created a directory for drush's cache, /tmp/drush/cache and set the environment variable CACHE_PREFIX as you suggested.
I suppose I could make /usr/local/share/drush/lib writable by www-data, but that violates several Debian rules. I thought about using /var/cache/drush for all users, but that opens a potential security hole for cache poisoning.
Comment #8
greg.1.anderson commentedNo, my fault. First, I should have tested in your exact configuration, and second, I should not have assumed that a "simple" change to drush_mkdir could just go in as a bugfix without review. It turns out that Drush assumes that it can call drush_mkdir($path) for any $path, working or not, without a drush_set_error being called. My previous change violated this assumption, which caused serious problems when certain directories are not writable. As you mention above, requiring these folders to be writable would be undesirable.
Please try the enclosed patch, which should work better. Note, however, that your test in #7 is incorrect. You must set CACHE_PREFIX like this:
You need both the patch here and the export CACHE_PREFIX in order for this to work.
Please also run all of the Drush tests for me. I ran them yesterday, but it was a pain; I'm out of town for a week and on a mifi connection that is slow and capped. I'll be back in town next week and will run them then, but you could speed things along by doing the tests for me.
Feedback from other maintainers on another point would also be helpful: should drush_mkdir call drush_set_error under any circumstances? Drush 4.x does not, and it seems that the existing Drush code does not always handle that correctly. It's hard to tell, under the current codebase, whether a failed call to drush_mkdir is a "test", and Drush will just go on to some other directory if the current call fails, or a requirement. The existing calls to drush_set_error were added because there are some instances where failures in creating required directories caused hard-to-track silent failures. Perhaps what we really need is two different variants of drush_mkdir -- drush_mkdir and drush_mk_required_dir, which work analogously to php's concept of 'include' and 'require'. It would take some effort to figure out which drush_mkdir needs to become the 'required' version, but I think it might be necessary.
Comment #9
dude4linux commentedInterestingly, I just found a drush cache in /tmp/.drush/cache owned by www-data and created a couple of days ago, apparently when I ran drush 4.5 as user www-data. Is there a reason why drush 5.0 needs a different location?
Comment #10
moshe weitzman commentedSeems like the function is more flexible if it does not throw an error. So, calls that need the error reported should do it themselves. Add a comment in doxygen for drush_mkdir instructing folks to throw own errors.
Comment #11
dude4linux commented@greg, I think you forgot to attach the patch.
Comment #12
greg.1.anderson commentedHere is the patch from #8 that I forgot to attach. It is not yet adjusted per #10.
re #9, Drush uses different caches for different purposes. /tmp/.drush/cache is, I believe, the extension cache, which I thought had been backed out of 5.x and 4.x alike. If not, that's a separate issue. /tmp/.drush is not a good cache location because it causes problems when you share drush between multiple users; if user A makes the cache dir, then user B cannot write to it.
Comment #13
dude4linux commentedGood news. With the patch in #12 applied, and not forgetting the export, I get the following results:
Now it's a matter of selecting a better location for the cache files. I have a couple of questions about that.
As a point of reference, I'm working on a Drupal 7 appliance for TurnKey Linux. See http://www.turnkeylinux.org/forum/general/20111227/tklpatch-drupal-7 for more details.
Comment #14
dude4linux commentedDoh. I forgot the third question.
3) Would it make sense to include a variable in drushrc.php to specify the location similar to how dump-dir specifies the location for sql dump files? This would avoid having to mess with the user environment.
Thanks again for all your hard work on drush.
Comment #15
greg.1.anderson commentedSetting your own cache location is niche enough that I think that the environment variable is fine. Usually Drush should just choose a reasonable location for you.
This new patch will use /tmp/drush/USERNAME/cache if other locations are not available. This avoids the unpleasant possibility that two different users might end up using the same cache directory. This also means that you will not need to explicitly set CACHE_PREFIX when calling drush cron from the www-data user unless you really want to.
I kept the error checking inside of drush_mkdir, because this function can be more specific than the caller can when an error is encountered. Permission errors are annoying (especially in support requests), and specific reporting is advantageous. Reporting is controlled by a $required parameter, that should be set to TRUE to enable reporting. It defaults to FALSE, which means that the caller should pick another location or handle the error themselves. I made a quick pass at explicitly setting $required for clients that were already well-behaved in this respect. Callers that are not doing adequate error checking I left alone.
If someone could run the unit tests for me... otherwise, I'll return to this next week.
Comment #16
moshe weitzman commentedNot applying cleanly for me.
Comment #17
greg.1.anderson commentedWhoops. Here's a better version.
Comment #18
moshe weitzman commentedI don't see where we change the signature for drush_mkdir(). I was checking for corresponding Doxygen add.
I got one failure:
Comment #19
dude4linux commented@greg The #15 patch applied over the #12 patch is working for me. I'm not up to speed on unit testing, so unless someone else decides to jump in, it will have to wait until you get back.
Comment #20
greg.1.anderson commentedThe doxygen change for drush_mkdir was in #15, but I accidentally left it off in #17. Here is yet another patch with the correct update.
phpunit pmDownloadTest.phpall worked for me. I guess I'll need to wait until I get back when I have the time and bandwidth to run everything clean and see what's up.n.b. drush/tests/README.txt has fairly good instructions on how to set up and run the unit tests.
Comment #21
greg.1.anderson commentedOh, I do think this patch is okay, but I will run all tests clean on Sunday or Monday to confirm.
Comment #22
moshe weitzman commentedlooks good to me.
Comment #23
greg.1.anderson commentedCommitted. I'm seeing one failure with and without this patch:
There was 1 failure:
1) siteUpgradeCase::testUpgrade
Unexpected exit code: /usr/bin/drush --root=/tmp/drush-sandbox/web --uri=dev --nocolor user-create example --mail='example@example.com' --password=password
Failed asserting that matches expected .
Comment #24
dude4linux commentedWell, with patches #12 & #15 installed, my cron ran all weekend with no problems. This morning, after seeing the fix had been committed, I ran 'git reset --hard' to remove the patches and 'git pull' to retrieve the latest version. Now I'm getting errors again.
Did I miss something?
Comment #25
greg.1.anderson commentedI'll look at it again.
Care to contribute some unit tests that fail when a key cache location is not writable? :)
Comment #26
dude4linux commented@greg: Here are the results I obtained from running the unit tests. The first attachment is from the unit tests run for drush-7.x-4.5 to test my setup. As expected all tests pass. The second attachment is the unit tests for drush-5.0-dev run as root, and the third attachment when run as user, www-data, with $PATH set to /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
Comment #27
greg.1.anderson commentedHm, running the unit tests as www-data is a good idea, and probably best from a pragmatic point of view. From a practical standpoint, though, I don't think this is a practice that the maintainers are going to get into on a regular basis. I was thinking that perhaps a test class that explicitly made the unit test's $HOME folder unwritable (and restored its permissions after the fact) and then did some tests in the unwritable state would be an ideal way to catch future defects in this class.
Comment #28
dude4linux commented@greg: Perhaps I misunderstood the meaning of your previous comment.
>> Care to contribute some unit tests that fail when a key cache location is not writable? :)
Writing a unit test for this use case is well beyond my abilities as a programmer. I'm happy to help in any way I can with testing and reporting problems.
It appears that you had the solution in patches #12 & #15 but that something was left out of the final commit. I've looked at the the patches and reviewed the git commit, but can't spot what is missing. Hopefully you'll have better luck being more familiar with the code than I.
I'll continue to monitor the thread and re-test any future changes.
Comment #29
greg.1.anderson commentedI was just careless somewhere in the commit; I'll fix it again, and write unit tests so it does not happen again, just as soon as I have a chance. Thanks for your feedback and help.
Comment #30
dude4linux commented@greg - Just to gather some more information, I ran a few more tests.
I started by going back to the commit prior to yours, i.e.
git checkout 3d6ba41then ran drush as www-data and as expected it failed. I next applied the #17 patch.
git apply -v drush-mkdir-17.patchIt applied successfully and the test passed. Next I tried the #20 patch.
Again it applied successfully and the test passed. Lastly, I tried your commit.
This time the test failed. Looks like the #20 patch was correct, but didn't get committed.
Comment #31
greg.1.anderson commentedDrush was requiring that the 'lib' dir exists and is writable. I changed the test to allow it to be write-protected, so long as it already exists.
Comment #32
dude4linux commented@greg - Thanks for getting back to this. All my tests on the latest dev release now pass and I'm able to run
drush core-cronas user=www-data. I think this issue can now be closed.
Comment #33
greg.1.anderson commentedThanks. Re-setting status to 'fixed'.
Comment #35
ChrisLaFrancis commentedEdit: nevermind.
Comment #36
ChrisLaFrancis commentedComment #37
dpearcefl commentedComment on #15:
I really did need to set the cache folder because of my specific situation. I added this to the drushrc.php file: