Setting the file access permissions for files and folders

development, server 1 Comment »

You have a web application hosted on a server and you want to set file access permissions by chmod command, different for files and folders.

Why different? A folder must have ‘x’ (access) flag:

drwxr-xr-x

At the same time a file shouldn’t have it (otherwise it would be executable):

rw-r--r--

How can you set it up for all nested files/folders in your application folder?

You can solve this task by setting common persmissions for all files and folders (step 1) and then make folders accessable (step 2), recursively:

chmod -R 644 .
chmod -R a+X .

But chmod allows you to set up only access flag for folders, you cannot set one persmission for folders and another for files.

Here is a script written by my collegue Anton [vojtoshik] Voitovych to solve this task.

Copy it and save to a file (I saved it in /bin/rchmod — thus it’s accessable for all users of my system):

#!/bin/bash

files="0644";
directories="0755";

path=$1;
shift;

while getopts "d:f:" OPTION
do
case $OPTION in
f) files="$OPTARG" ;;
d) directories="$OPTARG" ;;
esac
done

if [ "$path" != '' ]; then 
    find "$path" -type d ! -name "." -exec chmod "$directories" {} \;
    find "$path" -type f -exec chmod "$files" {} \;
else
    echo "Usage: rchmod <path_to_directory> [-d <directories mode>] [-f <files mode>]";
    echo -ne \\n;
fi

OK, now make it executable:

chmod 755 rchmod

How to use it.

1. You are in your application folder and want to set 755 (drwxr-xr-x) mode for folders and 644 (rw-r--r--) mode for files which is the default:

rchmod .

2. You want to set 123 permissions only for folders in a particular folder::

rchmod /some/path/to/folder -d 123

3. You want to set 123 permissions for folders and 456 permission for files in a particular folder::

rchmod /some/path/to/folder -d 123 -f 456

Note: don’t forget that every time after that command run you should make some folders writable (wp-content/uploads, cache, temp — what else you have in your webapp).

Zend Auth Session Expiration Time

development, php, zend Comments Off on Zend Auth Session Expiration Time

After authentication in my project it takes about 10-20 minutes for the auth session to expire, which is not handy — you go to get a snack and see a login screen coming back.

This is how to make the TTL of your auth session longer:

$s = new Zend_Session_Namespace( 
    $this->_auth->getStorage()->getNamespace() );

$s->setExpirationSeconds( strtotime('30 day', 0) );

Now your users will log in for 30 days.

You can call this code in your login action after successful authentication and before the redirect to the landing page.

Sharing work between agents

db, development, ideas, php Comments Off on Sharing work between agents

There are situations when you need to separate processing of big amount of data between several “agents”, e.g.:

  • you have a long list of websites which must be checked for being alive (404 error check) by your web-clawlers;
  • a queue of photos to be resized or videos to be converted;
  • articles that your editors must review;
  • catalogue of blog feeds that your system must import posts from;
  • etc.

The idea to do this is simple:

  1. Give a small piece of big work to an agent.
  2. Mark this piece as given to him (so that none other starts to do the same job) and remember the time stamp when the job was given or when the job becomes obsolete (this agent is dead, let’s give this job to someone else).
  3. If work is done — go to step #1.
  4. After some period of time (1 hour) check all the time stamps, and if some agents didn’t cope with the job, mark the jobs as free so that others could start to work on it.

The problem is between steps #1 and #2 — while you gave a job to Agent 1 and going to mark it as given to him, what if Agent 2 is given by the same job? If you have many Agents, this  can happen at real. This situations is called concurrent read/write.

To overcome this a lock can be used.

In this article I wil explain, how to use locks in Zend project with MySQL database.

First of all, MySQL documentation tells that SELECT .. FOR UPDATE can be used for that purpose. First step is to select records by that statement, and second step is to mark them as locked. Requirements are to use InnoDB storage and to frame these two statements in a transaction.

Happily, Zend_Db_Table_Select has a special method forUpdate() that implements SELECT .. FOR UPDATE statement. Zend_Db can cope with transactions as well. Let’s try it!

To lock a record, we need two fields:

  1. one to remember ID of agent that is processing this record (let’s call this column ‘locked_by‘)
  2. one another to know the time when the lock becomes obsolete (let’s call this column ‘expires_at‘)

I wrote a  class that inherits from Zend_Db_Table and helps to get records with locking them.

<?php

class Koodix_Db_Table_Lockable extends Zend_Db_Table
{
    protected $_lockedByField = 'locked_by';
    protected $_expiresAtField = 'expires_at';
    protected $_TTL = '1 HOUR'; //time to live for lock
    
    public function fetchLocked( Zend_Db_Table_Select $select, 
        $lockerID ) {
        
        $db = $this->getAdapter();
        $db->beginTransaction();
        
        $column = $db->quoteIdentifier( $this->_lockedByField );
        $select->forUpdate()
             ->where("$column=? OR $column IS NULL", $lockerID);


        $data = $this->fetchAll($select);
        if( empty($data) ) return null;
        
        $expiresAt = new Zend_Db_Expr('DATE_ADD( NOW(), 
            INTERVAL ' . $this->_TTL . ')');
        if( sizeof($this->_primary) > 1 ) {
            foreach( $data as $item ) {
                $item->{$this->_lockedByField} = $lockerID;
                $item->{$this->_expiresAtField} = $expiresAt;
                    
                $item->save();
            }
        }
        else {
            $arrIds = array();
            foreach( $data as $item ) {
                $arrIds[] = $item->id;
            }
            
            $this->update(
                array(
                    $this->_lockedByField => $lockerID,
                    $this->_expiresAtField => $expiresAt,
                ), 
                $db->quoteIdentifier(current($this->_primary)) . 
                    ' IN ("'.implode('","', $arrIds).'")'
            );
        }
        
        $db->commit();
        return $data;
    }

    public function releaseLocks( ) {
        
        $column = $db->quoteIdentifier( $this->_expiresAtField );
        
        return $this->update(
            array(
                $this->_lockedByField => null,
                $this->_expiresAtField => null,
            ), 
            "$column <= NOW()"
        );
        ;
    }
}

If the table has a composite primary key (containing more than one column), the ActiveRecord approach is used, so the save() method for every record is called, that’s simple (drawback — multiple update queries). Otherwise, if it is a deep-seated table with one ID column as a primary key, then the IDs are collected in a list and all records are updated by a single statement with IN in where clause (which is much faster).

TTL (‘Time to Live‘) — period of time when lock is allowed. In my application the default is one hour. Format of TTL can be seen in MySQL documentation.

And now how to use it.

Let’s imagine you have several editors that divide the big articles list and review them. My model class has a method fetchForUser() that returns no more than 5 articles for current user (by given user ID).

This is an Article table model, inherited from the class above. Usually such classes are located at

application/default/models/ArticleTable.php
<?php
class ArticleTable extends Koodix_Db_Table_Lockable
{
    protected $_name = 'article';
    
    public function fetchForUser( $userId, $count=5 ) {
        
        $select = $this->select()
            ->where('reviewed = 0')
            ->order('expires_at DESC')
            ->order('date_imported DESC')
            ->limit( $count );
        
        return $this->fetchLocked($select, $userId);
    }
}

Note: if the editor refreses the page, the expres_at fields is refreshed by current time as well.

As for step four of our algorithm (releasing all obsolete locks) — create an action in your backend controller, call your table model releaseLocks() method in it and call that action periodically by Cron.

To boost the performance of the lock releasing, create an index on the expires_at column. (Because of this reason I rejected the ‘locked_since‘ column in favor of ‘expires_at‘)

P.S. In my database date/time columns have DATETIME type. If you use INT to store timestamps, convert it to unix time and back.

assembla.com – free development tools

assembla, development Comments Off on assembla.com – free development tools

All sources for my development projects I store at assembla.com — very cool service for dvelopers, offering both free and paied services.

assembla

Free users have almost the same services scope just limited by space:

  • projects (in free plan the source is open for anyone), milestones and tickets
  • SVN/Git repository (you can close/refer tickets by commit messages)
  • wiki with several mark-up languages
  • team collaboration tools
  • agile tools

I started from hosting files of my FireFox addons there — SVN and wiki make it perfect choise.

Then I got used to the handy and comfortable interface so much that became a paied user — for xUSSR person it’s worth mentioning 🙂

And the last but not the least — Assembla team is very open and keeps in touch with their users.

WP Theme & Icons by N.Design Studio
Entries RSS Comments RSS Log in