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

greg.1.anderson’s picture

Status: Active » Fixed

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

dude4linux’s picture

Greg, 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.

greg.1.anderson’s picture

Status: Fixed » Postponed (maintainer needs more info)

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

dude4linux’s picture

Status: Active » Postponed (maintainer needs more info)

Here are the results when I follow the instructions in drush topic docs-cron:

# git checkout 7.x-4.5
# drush version
drush version 4.5
# su - www-data
$ which drush
/usr/local/bin/drush
$ /usr/local/bin/drush cron
Cron run successfully.                                                                                                                      [success]
$ exit

# git checkout master
Switched to branch 'master'
# drush version
drush version 5.0-dev
# su - www-data
$ echo $HOME  
/var/www
$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games
$ /usr/bin/env PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin COLUMNS=72 /usr/local/bin/drush cron
Unable to write in /var/www. Please check directory          [error]
permissions.

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.

greg.1.anderson’s picture

Status: Postponed (maintainer needs more info) » Active

I was relying on drush_mkdir to determine if the location is writable, but drush_mkdir calls drush_set_error.

greg.1.anderson’s picture

Title: drush cron fails when run as apache2 user www-data » Make cache directory selection more robust in situations where selected location is not writable
Status: Postponed (maintainer needs more info) » Fixed

I 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/cache in 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.

dude4linux’s picture

Status: Fixed » Active

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

# su - www-data
$ mkdir -p /tmp/drush/cache
$ CACHE_PREFIX=/tmp/drush/cache
$ drush version
Directory /usr/local/share/drush/lib exists, but is not writable.    [error]
Please check directory permissions.
$ CACHE_PREFIX=/tmp/drush 
$ drush version
Directory /usr/local/share/drush/lib exists, but is not writable.    [error]
Please check directory permissions.

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.

greg.1.anderson’s picture

Status: Active » Needs review

No, 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:

$ CACHE_PREFIX=/tmp/drush
$ export CACHE_PREFIX
$ drush version

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.

dude4linux’s picture

Status: Needs review » Active

Interestingly, 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?

moshe weitzman’s picture

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

dude4linux’s picture

@greg, I think you forgot to attach the patch.

greg.1.anderson’s picture

Status: Active » Needs work
StatusFileSize
new511 bytes

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

dude4linux’s picture

Good news. With the patch in #12 applied, and not forgetting the export, I get the following results:

# su - www-data
$ CACHE_PREFIX=/tmp/drush
$ export CACHE_PREFIX
$ drush version
drush version 5.0-dev
$ drush cron
Cron run successful.                                                                                                                        [success]
$ ls -l /tmp/drush/cache
total 4
drwxr-xr-x 2 www-data www-data 4096 Jan 12 12:41 default
$ ls -l /tmp/drush/cache/default
total 12
-rw-r--r-- 1 www-data www-data 1699 Jan 12 12:41 5.0-dev-commandfiles-0-eaf5e7c2621e2c8c454a5e2e36d70645.cache
-rw-r--r-- 1 www-data www-data  183 Jan 12 12:41 5.0-dev-commandfiles-2-c32acd775c18a90c5b41d0fc9accf5ab.cache
-rw-r--r-- 1 www-data www-data  122 Jan 12 12:41 5.0-dev-commandfiles-5-e595d987acd5ba595d190642e65183e6.cache

Now it's a matter of selecting a better location for the cache files. I have a couple of questions about that.

  1. 1) Is it desirable to share the cache files between users with different privileges? My concern would be that with the www-data user having write privileges to the cache, would it be possible to trick apache into overwriting a file that would later be used by root or another user with higher privileges running drush. Maybe I'm being too paranoid.
  2. 2) On a Linux system, is /var/cache/drush a reasonable location?

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.

dude4linux’s picture

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

greg.1.anderson’s picture

Status: Needs work » Needs review
StatusFileSize
new7.6 KB

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

moshe weitzman’s picture

Status: Needs review » Needs work

Not applying cleanly for me.

greg.1.anderson’s picture

Status: Needs work » Needs review
StatusFileSize
new6.07 KB

Whoops. Here's a better version.

moshe weitzman’s picture

Status: Needs review » Needs work

I don't see where we change the signature for drush_mkdir(). I was checking for corresponding Doxygen add.

I got one failure:

1) pmDownloadCase::testDestination
Failed asserting that file "/tmp/drush-sandbox/web/sites/dev/modules/devel/README.txt" exists.

/Users/mweitzman/c/h/drush/tests/pmDownloadTest.php:47
dude4linux’s picture

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

greg.1.anderson’s picture

StatusFileSize
new7.35 KB

The 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.php all 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.

greg.1.anderson’s picture

Status: Needs work » Needs review

Oh, I do think this patch is okay, but I will run all tests clean on Sunday or Monday to confirm.

moshe weitzman’s picture

Status: Needs review » Reviewed & tested by the community

looks good to me.

greg.1.anderson’s picture

Status: Reviewed & tested by the community » Fixed

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

dude4linux’s picture

Well, 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.

# git show
commit a57d92d2c087266d9b41f42a29efe0d4e5be9bea
Author: Moshe Weitzman <weitzman@tejasa.com>
Date:   Mon Jan 16 09:04:49 2012 -0500
...
# su - www-data
$ drush version
Directory /usr/local/share/drush/lib exists, but is not writable.    [error]
Please check directory permissions.
$

Did I miss something?

greg.1.anderson’s picture

Assigned: Unassigned » greg.1.anderson
Status: Fixed » Needs work

I'll look at it again.

Care to contribute some unit tests that fail when a key cache location is not writable? :)

dude4linux’s picture

@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

greg.1.anderson’s picture

Hm, 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.

dude4linux’s picture

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

greg.1.anderson’s picture

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

dude4linux’s picture

@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 3d6ba41
then ran drush as www-data and as expected it failed. I next applied the #17 patch.
git apply -v drush-mkdir-17.patch
It applied successfully and the test passed. Next I tried the #20 patch.

git reset --hard
git apply -v drush-mkdir-20.patch

Again it applied successfully and the test passed. Lastly, I tried your commit.

git reset --hard
git checkout 47d4008

This time the test failed. Looks like the #20 patch was correct, but didn't get committed.

greg.1.anderson’s picture

Status: Needs work » Fixed

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

dude4linux’s picture

Assigned: greg.1.anderson » Unassigned
Status: Fixed » Needs work

@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-cron
as user=www-data. I think this issue can now be closed.

greg.1.anderson’s picture

Status: Needs work » Fixed

Thanks. Re-setting status to 'fixed'.

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

ChrisLaFrancis’s picture

Status: Closed (fixed) » Needs work

Edit: nevermind.

ChrisLaFrancis’s picture

Status: Needs work » Closed (fixed)
dpearcefl’s picture

Version: » 7.x-5.8

Comment on #15:

Setting your own cache location is niche enough that I think that the environment variable is fine.

I really did need to set the cache folder because of my specific situation. I added this to the drushrc.php file:

putenv('CACHE_PREFIX=/home/staff/shared/drush');