phaqphaq

“a geeks daily life”

Archive for the 'Programming' Category

Recognize invalid/unexpected characters with Perl

Tuesday, October 20th, 2009

Today a colleague of mine faced a very weird problem.
While parsing XML output from an HP ILO into Perl, his code constantly broke with the message:

FILE.XML:123 parser error : PCDATA invalid Char value 1

While the message itself states clearly that there is an unexpected character value (Char value 1, ASCII SOH) on one hand, it doesn’t tell the character position on the other.

Looking at the input string itself on the console, it wasn’t obvious either:

<EVENT SEVERITY=”Caution” LAST_UPDATE=”08/03/2009 22:20″ INITIAL_UPDATE=”08/03/2009 22:20″ COUNT=”1″ DESCRIPTION=”POST Error: ” />

So I proposed to add some lines to help identify the character position on the given input string, which was basically this:

@array = unpack(”C*”, $_my_input_var);
foreach (@array) {
printf(”char \”%s\” is ord %s\n”, chr($_), $_);
}

This led to the following output:

char “<" is ord 60
char "E" is ord 69
char "V" is ord 86
--- some output omitted ---
char ":" is ord 58
char " " is ord 32
char "" is ord 1
char """ is ord 34
char " " is ord 32
char "/" is ord 47
char ">” is ord 62

So looking at this we saw that ASCII char 1 (which is an unprintable character, it will be represented as ^A in some editors like vi) was the fifth character before the end of the string.

Well, basically the solution to this is to apply an additional input filter to remove ASCII char 1 like this:

$_my_input_var =~ s/\x01//g;

While this solves just _this_ problem, a more solid solution is to remove all non-printable characters as well, given the list of ASCII characters at http://www.asciitable.com/.

So a filter like this may apply, removing all non-printable characters, leaving just a few control characters 1×08 to 1×1F (Tab, Carriage Return, Line Feed and a few others) and the printable characters in it.

$_my_input_var =~ s/[\x00-\x08\x0B-\x1F\x7F-\xFF]//g;

Strange compilation error on MySQL

Thursday, July 16th, 2009

Yesterday I started digging around for a solution to create per-user or per-database statistics on MySQL, one of the more important peaces I was missing from it for a long time.

Luckily enough, some guys over there had already done some work on this topic, so I wouldn’t have to start over from scratch :-)

It took me only little time to port over the patch from MySQL 5.0.51 to the more current 5.0.83 release within the FreeBSD ports tree, however not soon after starting a build I would encounter this error message:

[root@bld-bsd-224-221 /usr/ports/databases/mysql50-server]# make build
[ - some output omitted - ]
/bin/sh ../ylwrap sql_yacc.yy y.tab.c sql_yacc.cc y.tab.h sql_yacc.h y.output sql_yacc.output --  -d -d --debug --verbose
-d: not found
*** Error code 1

At first I thought the port was corrupted so I refetched the package and reapplied the patch, to no avail.
So I tried again using the original port without having the patch applied, which worked flawlessly.

At second glance I checked for the file list from above command and noticed that it included the file named sql_yacc.yy, one of which had been altered by the previously applied patch.
My conclusion was that the file had been wrongly patched, containing a syntax error or such alike.
I then extracted the unpatched package once more to do a clean rebuild without patches.
I checked the compilation output for the above command line, only to note that it wasn’t actually there!

The question was: Why would the command line “/bin/sh ../ylwrap sql_yacc.yy ….” not get invoked when doing a build on a clean, unpatched package?
I double-checked my patches to see if the command was introduced by itself, which was not the case. That single command actually belongs to the stock MySQL Makefile.

At that stage I decided to just add a single whitespace to the file sql_yacc.yy and run the command manually:

[root@bld-bsd-224-221 /usr/ports/databases/mysql50-server/work/mysql-5.0.83/sql]# make
/bin/sh ../ylwrap sql_yacc.yy y.tab.c sql_yacc.cc y.tab.h sql_yacc.h y.output sql_yacc.output --  -d -d --debug --verbose
-d: not found
*** Error code 1

Interestingly enough that command actually only seems to get involved when the contents of the sql_yacc.yy file is altered.
As such the error was indeed not caused by the patch itself.

So I digged deeper in analyzing the “ylwrap” script file, which is included with the MySQL package. Oh well, at that time I really felt like an idiot!
When I realized that this seemed to by a wrapper script for YACC I also noticed the double-hyphen, which is really an indicator for subsequent command line arguments to be passed on to a sub-process.
Having said that I supposed there was actually missing something in between here: “– {HERE} -d -d”
Could it be that it’s missing the command name of the YACC sub-processor?

Well, do I have YACC installed?


bld-bsd-224-221.genotec.ch:/usr/ports/databases/mysql50-server# which yacc
/usr/bin/yacc

Well, I do … the only catch is: MySQL depends on bison, not on YACC.
To make things worse, neither the FreeBSD port Makefile nor the MySQL configure script check on that dependency, most likely as it is *usually* not required.

Good catch, after having installed bison from the ports tree MySQL compiled like a charm even with all my patches applied :-)

Apple’s Safari violates RFC2616

Wednesday, January 23rd, 2008

Today I faced an issue, where HTTP redirections didn’t work out as expected on Apple’s Safari browser.

This came up while I was coding up some sort of web-based login redirector, which is stacked up in three layers:

  1. html login form
  2. login preprocessor (server side scripting)
  3. login processor (server side scripting)

This solution was required to implement a generic way to create branded login forms, which will send their login requests to a unique, centralized login preprocessor, which will – after doing some internal magic – redirect to the final login processor.

Now, the login preprocessor was set to do redirects to the final login processor using HTTP/1.1 temporary redirects (http reply code 307) to preserve already existing POST data.
While this was working out properly on most browser, I stumbled accross Safari, which will silently discard all POST data.

Checking out the access logs revealed some interesting facts.
The first excerpt shows the access as performed by Firefox:

192.168.0.2 - - [23/Jan/2008:16:32:02 +0100] "POST / HTTP/1.1" 307 20 "https://xyz/" "Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11"
192.168.0.2 - - [23/Jan/2008:16:32:05 +0100] "POST /some_other_location HTTP/1.1" 200 7934 "https://xyz/" "Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11"

As required by the 307 temporary redirect Firefox will resubmit to the new location using a POST request (preserving existing POST data).
The first request actually refers to stage 2 (login preprocessor), the second request reflects the resubmission to the final login processor (step 3).

Now this is was Safarie does:

192.168.0.3 - - [23/Jan/2008:16:31:26 +0100] "POST / HTTP/1.1" 307 20 "https://xyz" "Mozilla/5.0 (Windows; U; Windows NT 5.1; de-DE) AppleWebKit/523.15 (KHTML, like Gecko) Version/3.0 Safari/523.15"
192.168.0.3 - - [23/Jan/2008:16:31:26 +0100] "GET /some_other_location HTTP/1.1" 200 7931 "https://xyz" "Mozilla/5.0 (Windows; U; Windows NT 5.1; de-DE) AppleWebKit/523.15 (KHTML, like Gecko) Version/3.0 Safari/523.15"

As we can see, it also receives a 307 temporary redirect from the server and follows it (which is correct as of RFC2616) in the first request (again step 2 as outlined before).
However, the second request is resubmitted using GET, which means that all previously existing POST data is lost (this is step 3 as outlined before).

In this case, Safari clearly violates RFC2616, which states in Chapter 10.3.3 302 Found:


RFC 1945 and RFC 2068 specify that the client is not allowed
to change the method on the redirected request. However, most
existing user agent implementations treat 302 as if it were a 303
response, performing a GET on the Location field-value regardless
of the original request method. The status codes 303 and 307 have
been added for servers that wish to make unambiguously clear which
kind of reaction is expected of the client.

As a side note to this excerpt from the RFC I shall note, that status code 302 Found as referred-to by RFC2616 used to be
302 Moved Temporarily (as of RFC2068), which has been redefined to 307 Temporary Redirect.

As such, while status code 303 See Other clearly states, that GET should be used upon redirect, statements made in
RFC2616’s Chapter 10.3.3 302 Found also apply to 307 Temporary Redirect.

In this case it means, that Safari violates the standard when it changes the access request method to GET.

No “sleep” command for batch files? Make it a choice!

Saturday, May 5th, 2007

I just trapped myself while hacking up a batch file.
Used to shell scripting I wanted to add a delay to the batch using “sleep”.

Dough! Bad Idea! Bad command or filename. Smash your head here to continue {(x)}!

So I winded up my memories from stoneage. Wasn’t there the choice command!?

Yeah, after some lurking around with the ‘/?’ feature I had stuck it together:

choice /c 1 /d 1 /t 1 > nul

While

  • “/c 1″ sets the choice values (1 is my value)
  • “/d 1″ sets the default choice value (which is 1 from above)
  • “/t 1″ sets the timeout to 1 second (or whatever is appropriate)
  • “> nul” means the same as “>/dev/null”: send output to nirvana (notice there being only one ‘l’ however)

Of course this may be bothersome to type if you use it often, so a “batch function” may be better, especially when you need other batch tricks to get around DOS command limitations (lazy man’s approach: create a second batch file for it).

@echo off

rem *******************
rem check args
rem *******************

:checkargs

if "%1/" == "func/" goto callfunc
goto main

:_checkargs

rem *******************
rem call functions
rem *******************

:callfunc
 shift

 rem we could do "goto %1" instead
 rem if there is a lot of functions
 if "%1/" == "sleep/" goto sleep

 goto exit

:_callfunc

rem *******************
rem function sleep
rem *******************

:sleep
 shift

 choice /c 1 /d 1 /t %1 > nul

 goto exit

:_sleep

rem *******************
rem main body
rem *******************

:main
 echo hello, going to sleep now
 call %0 func sleep 1

 echo sleep is over, good bye

 goto exit

:_main

rem *******************
rem exit handler
rem *******************

:exit
 rem if there is anything left to do, do it now.

:_exit

Your Makefile has been rebuilt…

Wednesday, January 10th, 2007

One might asume building a port from source should be fast and straight forward.
Not in this case, however…

# cd /usr/ports/devel/p5-File-Tail
# make clean build
===> Cleaning for perl-5.8.8
===> Cleaning for p5-File-Tail-0.99.3
===> Vulnerability check disabled, database not found
===> Extracting for p5-File-Tail-0.99.3
=> MD5 Checksum OK for File-Tail-0.99.3.tar.gz.
=> SHA256 Checksum OK for File-Tail-0.99.3.tar.gz.
===> p5-File-Tail-0.99.3 depends on file: /usr/local/bin/perl5.8.8 – found
===> Patching for p5-File-Tail-0.99.3
===> p5-File-Tail-0.99.3 depends on file: /usr/local/bin/perl5.8.8 – found
===> Applying FreeBSD patches for p5-File-Tail-0.99.3
===> p5-File-Tail-0.99.3 depends on file: /usr/local/bin/perl5.8.8 – found
===> Configuring for p5-File-Tail-0.99.3

File::Tail will be installed without debugging information.
This information isn’t usefull unless you intend to tinker
with the code. To install with debugging enabled, use:
perl Makefile.PL LOGIT
Checking if your kit is complete…
Looks good
Writing Makefile for Mail
==> Your Makefile has been rebuilt. <==
==> Please rerun the make command. <==
false
*** Error code 1

Stop in /usr/ports/devel/p5-File-Tail/work/File-Tail-0.99.3

Usually, this particular error happens only if local time/date is not set correctly.

Good, so show me the date, please:

# date
Wed Jan 10 11:49:18 CET 2007

But what is this? Date and time seems right.
Maybe something wrong with the files within the port directory?

# cd /usr/src/devel/p5-File-Tail
# make clean && make extract
# find ./work/File-Tail-0.99.3/ -type f -exec ls -l {} \;
-rw-r--r-- 1 root wheel 452 Sep 14 2005 ./META.yml
-rw-r--r-- 1 root wheel 1931 Dec 18 1998 ./t/20tail.t
-rw-r--r-- 1 root wheel 912 Sep 14 2005 ./t/30name_change.t
-rw-r--r-- 1 root wheel 1653 Dec 18 1998 ./t/10open.t
-rwxr-xr-x 1 root wheel 1101 Feb 2 1999 ./select_demo
-rw-r--r-- 1 root wheel 27994 Jan 10 11:47 ./Tail.pm.debug
-rw-r--r-- 1 root wheel 4812 Sep 14 2005 ./Changes
-rwxr-xr-x 1 root wheel 1381 Nov 3 1999 ./logwatch
-rw-r--r-- 1 root wheel 198 Sep 14 2005 ./MANIFEST
-rw-r--r-- 1 root wheel 25311 Jan 10 11:47 ./Tail.pm
-rw-r--r-- 1 root wheel 2471 Oct 17 2000 ./Makefile.PL
-rw-r--r-- 1 root wheel 2143 Feb 2 1999 ./README
-rw-r--r-- 1 root wheel 27991 Sep 14 2005 ./Tail.pm.debug.orig
-rw-r--r-- 1 root wheel 23027 Jan 10 11:47 ./Makefile
-rw-r--r-- 1 root wheel 0 Jan 10 11:47 ./blib/lib/File/.exists
-r--r--r-- 1 root wheel 25311 Feb 9 11:47 ./blib/lib/File/Tail.pm
-rw-r--r-- 1 root wheel 0 Jan 10 11:47 ./blib/lib/auto/File/Tail/.exists
-rw-r--r-- 1 root wheel 0 Jan 10 11:47 ./blib/arch/.exists
-rw-r--r-- 1 root wheel 0 Jan 10 11:47 ./blib/arch/auto/File/Tail/.exists
-rw-r--r-- 1 root wheel 0 Jan 10 11:47 ./blib/bin/.exists
-rw-r--r-- 1 root wheel 0 Jan 10 11:47 ./blib/script/.exists
-rw-r--r-- 1 root wheel 0 Jan 10 11:47 ./blib/man1/.exists
-rw-r--r-- 1 root wheel 0 Jan 10 11:47 ./blib/man3/.exists
-rw-r--r-- 1 root wheel 14740 Jan 10 11:47 ./blib/man3/File::Tail.3
-rw-r--r-- 1 root wheel 0 Jan 10 11:47 ./pm_to_blib

Well, nothing bad here either.
However, it *has* to be something about the date.

Maybe, there is some issue with perl libs, MakeMaker in
particular, then?

Let's see:

# find /usr/local/lib/perl5/ -name "*MakeMaker*" -exec ls -l {} \;
-r--r--r-- 1 root wheel 78826 Feb 9 08:48 /usr/local/lib/perl5/5.8.8/ExtUtils/MakeMaker.pm
total 20
-r--r--r-- 1 root wheel 565 Feb 9 08:48 Config.pm
-r--r--r-- 1 root wheel 7605 Feb 9 08:48 FAQ.pod
-r--r--r-- 1 root wheel 4387 Feb 9 08:48 Tutorial.pod
-r--r--r-- 1 root wheel 769 Feb 9 08:48 bytes.pm
-r--r--r-- 1 root wheel 707 Feb 9 08:48 vmsish.pm
-rw-r--r-- 1 root wheel 19874 Feb 9 08:49 /usr/local/lib/perl5/5.8.8/perl/man/man3/ExtUtils::MakeMaker.3.gz
-rw-r--r-- 1 root wheel 1866 Feb 9 08:49 /usr/local/lib/perl5/5.8.8/perl/man/man3/ExtUtils::MakeMaker::Config.3.gz
-rw-r--r-- 1 root wheel 1983 Feb 9 08:49 /usr/local/lib/perl5/5.8.8/perl/man/man3/ExtUtils::MakeMaker::bytes.3.gz
-rw-r--r-- 1 root wheel 3631 Feb 9 08:49 /usr/local/lib/perl5/5.8.8/perl/man/man3/ExtUtils::MakeMaker::Tutorial.3.gz
-rw-r--r-- 1 root wheel 5239 Feb 9 08:49 /usr/local/lib/perl5/5.8.8/perl/man/man3/ExtUtils::MakeMaker::FAQ.3.gz
-rw-r--r-- 1 root wheel 1937 Feb 9 08:49 /usr/local/lib/perl5/5.8.8/perl/man/man3/ExtUtils::MakeMaker::vmsish.3.gz
-r--r--r-- 1 root wheel 3905 Feb 9 08:49 /usr/local/lib/perl5/5.8.8/BSDPAN/ExtUtils/MakeMaker.pm

There it is! You see?
Obviously the was a timestep forward one the system clock sometimes in the past, so files created then actually got a timestamp in the future.

So let's fix this and try rebuilding the port.

# find /usr/local/lib/perl5/ -name "*MakeMaker*" -exec touch {} \;
# cd /usr/ports/devel/p5-File-Tail
# make clean build
===> Cleaning for perl-5.8.8
===> Cleaning for p5-File-Tail-0.99.3
===> Vulnerability check disabled, database not found
===> Extracting for p5-File-Tail-0.99.3
=> MD5 Checksum OK for File-Tail-0.99.3.tar.gz.
=> SHA256 Checksum OK for File-Tail-0.99.3.tar.gz.
===> p5-File-Tail-0.99.3 depends on file: /usr/local/bin/perl5.8.8 – found
===> Patching for p5-File-Tail-0.99.3
===> p5-File-Tail-0.99.3 depends on file: /usr/local/bin/perl5.8.8 – found
===> Applying FreeBSD patches for p5-File-Tail-0.99.3
===> p5-File-Tail-0.99.3 depends on file: /usr/local/bin/perl5.8.8 – found
===> Configuring for p5-File-Tail-0.99.3

File::Tail will be installed without debugging information.
This information isn’t usefull unless you intend to tinker
with the code. To install with debugging enabled, use:
perl Makefile.PL LOGIT
Checking if your kit is complete…
Looks good
Writing Makefile for File::Tail
===> Building for p5-File-Tail-0.99.3
cp Tail.pm blib/lib/File/Tail.pm
Manifying blib/man3/File::Tail.3

So we learn an important lesson from this: Perl’s MakeMaker does not only fail the build process if the system clock is set incorrectly.
It does so also if the timestamps on any source files and MakeMaker’s own perl modules have a time leap forward.