Text

PHP Tail File

Why tail a file?

Sometimes there’s a need to tail log files. This might be to store data in a more structured form, say in a database. Maybe you need to tail al log file to monitor for a certain condition. These log files could be anything, like maybe Apache or Mail logs.

PHP doesn’t come with any easy way built in to tail a file. Unix comes with the handle tail tool. I use tail regularly mixed with a grep, looking for a certain entry which I know will appear.

// tail the apache log only showing line containing the local IP.
$ tail -f /var/log/apache.log | grep 127.0.01

Example

// create tail object with a file containing our log data
$tail = new FileTail('/var/log/apache.log');

// if we have a previous position we ran up to restore that position
if (is_file('pos')) {
    $tail->setPos(file_get_contents('pos'));
}

// while we can read aline
while ($line = $tail->readLine()) {
    // store where we've got to
    file_put_contents('pos', $tail->getPos());

    // use a regular expression to read values out of the line
    ....
    // use a database connection to store 
    ....
}

This code would run until stopped. With blocking turned on, TailFile will wait and keep checking for newly appended lines.

Source Code

Tags: php tail file
Link

One of the best PHP shells I’ve seen. Made by the development team at Facebook.

Unfortunately mostly written in python, but thats available on most systems.

Tags: php shell
Text

PHP unix man pages

A recent post from php.net explains a tool I didn’t know existed pman. Unix man pages of PHP functions.

This little tool makes accessing manual pages for PHP functions incredibility fast.

Installation is very easy:

    # using pear to install the tool
    $ pear install doc.php.net/pman

    # use the command pman followed by the function name
    $ pman strlen

This example instantly displays a unix style man page loaded locally of the strlen docs.

This is a great way of keeping a local copy of PHP docs for those times when you can’t access php.net.

I also find the unix style man docs easier to read. After all you only need to get used to reading one style of manual page (although php.net manual style has been some of the best I’ve come across).

Tags: php manual
Text

Using strace to debug PHP

The Problem

Recently I stumbled into an issue with the PHP FTP functions. After successfully connecting to a server and authenticating, fetching a file list using ftp_nlist would return NULL. This isn’t even a return type listed in the manual.

Example

$ftp_server = 'ftp.example.com';

// set up basic connection
$conn_id = ftp_connect($ftp_server) or die("Couldn't connect to $ftp_server\n");

$ftp_user_name = 'user';
$ftp_user_pass = 'pass';

// login with username and password
$login_result = ftp_login($conn_id, $ftp_user_name, $ftp_user_pass) or die("Login failed");

// get contents of the current directory
$contents = ftp_nlist($conn_id, ".");

// output $contents
var_dump($contents);

Why was it failing? It was connecting fine, there where no problems with authenticating.

As a developer getting into this situation is very frustrating. Where do we turn to?

The Solution (strace)

What is strace

One solution is strace (presumably short for “system trace”), a system debugging tool for linux. There are other Unix variants like dtruss and dtrace. In this article we’ll concentrate on strace.

The tool traces system calls and signals made by the process you tell it to trace. The name of each system call, its arguments and its return value are printed on standard error, which isn’t pretty. I find it helpful to output the results to a file (-o filename argument to strace can help), alternatively you can redirect standard error to standard out and pipe the results though grep.

How to use strace

There are two ways you can tell it to trace a process, which depends on if the process is already running or not.

Trace an already running process

Attach to the process with the process ID pid and begin tracing. The trace can be terminated at any time by a keyboard interrupt signal (ctrl-c). strace will detach itself from the process leaving it to continue running. This can be handy to look at what a long running script is currently doing. This includes things like Apache.

// -p pid
$ strace -p 123
Trace a specified command

The other way we can trace something is by telling it the command we want executed and traced. Anything which is not one of its own arguments is the command it will execute, anything after that are arguments to the executing command.

// -e is an argument of strace with the value of open
// -v is an argument of php with no value
$ strace -e open php -v 2>&1 | grep php.ini
// outputs the location of the php.ini file which was loaded
Understanding the output

Running the command:

strace -e open php -v 2>&1 | grep php.ini

Outputs:

open("/usr/bin/php.ini", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/etc/php5/cli/php.ini", O_RDONLY|O_LARGEFILE) = 3

Here we see that PHP attempted to open two files. One in directory where the binary is, and one in /etc/php5/cli/ this is where PHP has been compiled to look. The first system call failed with No such file or directory.

You can learn more about system calls and their return values from the linux documentation. http://linux-documentation.com/en/man/man2/open.html

Strace & PHP Continued…

Find php.ini

Example Command:

// -e open, trace only the open system call
// -v ask php to show it's version
// 2>&1 redirect stderr to stdout
// | grep php.ini filter the output for the string php.ini
$ strace -e open php -v 2>&1 | grep php.ini
// outputs the location of the php.ini file which was loaded :)

Example Output:

open("/usr/bin/php.ini", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/etc/php5/cli/php.ini", O_RDONLY|O_LARGEFILE) = 3

php.ini is being loaded from /etc/php5/cli/php.ini

File not loading

Example Command:

strace -e open php test.php 2>&1 | grep include.php

Example Output:

open("/usr/bin/include.php", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)

Oops maybe we have the wrong path to include the file.

Fixing the problem

$ strace -s 300 -e connect,send,recv php ftpTest.php
  • -s 300 (Specify the maximum string size to print the default is 32)
  • -e connect,send,recv (Trace only the specified set of system calls)

The output from this strace showed that ftp_nlist was asking the remote server to connect in on a local IP. Clearly an internet ftp server can’t connect in to our local ip. PHP’s error handling on this is clearly insufficient, but thanks to the handy functionality of strace was easy to find. In this case the fix was to make the connection pasv.

Helpful links