Front-end engineering and so on


Linux Cron: Customization of Email Subject isn't implemented

Question: How to customize cron emails' subject?
Answer: It's hardcoded. That's it.

The only one thing is configurable: MAILTO.
Content-Type is text/plain. Charset is taken from current locale.
Email subject is built using commanline configured in /etc/crontab or just hostname (in case of anacron)
If you really need different subjects at least with prefixes "ALERT" or "WARNING" - feel free to send mail through your own way.

This is how it's hardcoded in crond:

Source code: src/do_command.c, line 446

This is how it's hardcoded in anacron:

Source code: anacron/runjob.c, line 274


How to delete a closed Sprint in JIRA Agile

For now there is no feature in JIRA Agile which allows to delete closed sprints.
Atlassian has a ticket to implement it, but its status "OPEN": GHS-9220

There is a way to delete sprint by using using JIRA REST API.
You can delete sprint by executing following command:

curl -u '{user}:{password}' '{sprintId}' -X DELETE -H 'Accept: application/json'
You need to replace:
{user} and {password}
with real JIRA user name and password
with real sprint ID
I checked this command for JIRA OnDemand (aka Atlassian Cloud).

Sprint ID can be found via JIRA UI as described in following article: How to rename a closed Sprint in JIRA Agile


Disabling PayPal Bill Me Later in Magento 1.9

Magento started to support new PayPal feature Bill Me Later in version 1.9
Everything would is good, but Magento enables BML automatically on next re-saving of payment methods settings if you're using Payflow Pro.

I didn't find a way of disabling it in Magento's System Configuration admin without disabling Payflow Pro completely. But, as usual there is another way :-) you can change it directly in MySQL.

Here how it looks in database in table core_config_data:

Run following query in MySQL console (or phpMyAdmin, MySQL Workbench or whatever you use):

UPDATE core_config_data SET value = 0 WHERE path IN ( 'payment/paypal_express_bml/active', 'payment/paypaluk_express_bml/active' );

Don't forget to clean configuration cache!


Magento: Delete unused product images

Magento media folder grows all the time. It collects many unused files, such as abandoned product images. I've found that we already reached 100GB of product images on one from our Magento projects and decided to clean it.

I looked for magento extensions capable to search for unused / abandoned product images and delete them. For instance, following extension works great, but it fails on catalog with more than 100k products: Magento Connect - Image Clean
It died in close to infinite loop of 500k files info scanning. After that, it would try to put all it to DB and only then render some user interface, which had to allow me to run cleaup procedure. I was limited in time, couldn't wait 10 hours for result and wrote own script, which is 1000% more efficient and works on any magento installation. In theory it should support all magento versions starting from CE 1.5

Idea is very simple:

  1. Dump all product image files from database:
    SELECT `value` FROM catalog_product_entity_media_gallery;

  2. Select list of files from magento/media/catalog/product/ excluding cache folder:
    cd /var/www/magento/media/catalog/product/
    find ? -type f

  3. Compare first to second list, find files unique for file system and remove them.

You can find final version of script here:

You'll need to change values of constants at start of script and it's ready to be used. I also added different validations and count-back to be able to cancel dangerous operation at last minute. The script was successfully used on production environment. It removed more than 140k images; it resulted in freeing 45GB of disk space.

Keep your Magento clean! :)


OpenSSL: Convert private key to PEM format for AWS ELB

You might get message "Error: Invalid Private Key" while configuring SSL on Elastic Load Balancer on Amazon Web Services (AWS). It means your private key isn't in PEM format. No worries, it easy to fix.

Standard CSR generation instructions, such as provided on GoDaddy support page, provide you private key file, which is not in PEM format:
openssl req -new -newkey rsa:2048 -nodes -keyout yourdomain.key -out yourdomain.csr

Converting private key into PEM format is quite simple with OpenSSL RSA key processing tool:
openssl rsa -in yourdomain.key -outform PEM -out yourdomain_pem.key

You can check first lines of key files to see difference:
[andrey@centos openssl_rsa]$ head -n 3 yourdomain.key yourdomain_pem.key
==> yourdomain.key <==

==> yourdomain_pem.key <==

That's it. You can copy content of PEM key file into ELB configuration dialog:

AWS ELB SSL Configuration dialog


Secure SSH: SshGuard init script for CentOS

I've tried to find good init script for SshGuard daemon for CentOS 6.3, but mainly found scripts this:

I've tried to improve script by adding different checks like existance of process, separated configuration with list of log files to be listened and white-listed IPs as it's usually done in init scripts in CentOS/RHEL.

Script itself (/etc/init.d/sshguard):
# sshguard      Start up the sshguard daemon
# chkconfig:    2345 56 24
# description:  SshGuard daemon
# processname:  sshguard
# pidfile: /var/run/

# source function library
. /etc/rc.d/init.d/functions

# pull in sysconfig settings
[ -f /etc/sysconfig/sshguard ] && . /etc/sysconfig/sshguard


args="$(for LOG in "${SSHGUARD_LISTEN_LOGS[@]}"; do echo -l $LOG; done) $(for IP in "${SSHGUARD_WHITELIST[@]}"; do echo -w $IP; done) $SSHGUARD_OPTIONS"

    echo -n $"Starting $prog: "
    [ -x $exec ] || {
        failure $"$base startup"
        echo "Cannot find executable: $exec" >&2
        exit 5

    [ -r ${pidfile} ] && {
        pid=$(cat ${pidfile})
        checkpid $pid && {
            failure $"$base startup"
            echo "sshguard already started: PID=$pid" >&2
            exit -1

    $exec $args &>/dev/null &
    echo $! > ${pidfile}
    success $"$base startup"
    return 0

    echo -n $"Stopping $prog: "
    killproc -p ${pidfile} $prog
    [ $retval -eq 0 ] && rm -f $lockfile
    return $retval

restart() {

case "$1" in
        status -p ${pidfile} ${prog}
        echo $"Usage: $0 {start|stop|restart|status}"
exit $RETVAL

Configuration file (/etc/sysconfig/sshguard):
# list of white listed IPs (bash array)
# list of listened log files (bash array)
SSHGUARD_LISTEN_LOGS=( /var/log/secure )
# additional command line options (string)

Download scripts as tar.bz2 archive →

In order to use this script you have to:
  1. Put the script by path /etc/init.d/sshguard
  2. Change owner and group to root:
    sudo chown root:root /etc/init.d/sshguard
  3. Make it executable:
    sudo chmod +x /etc/init.d/sshguard
  4. Add to startup configuration:
    sudo chkconfig --add sshguard 
  5. Add configuration file by path /etc/sysconfig/sshguard (see example above)
  6. Start sshguard:
    sudo service sshguard start
    (or restart|stop|status)


Gentoo - PHP 5.3.13 - SoapFault: Start tag expected, '<' not found

Surprisingly I've found that PHP's SoapClient doesn't work properly on my laptop with Gentoo Linux.

Sample code:
$client = new SoapClient("http://localhost/wsdl.xml");

Exception text:
SoapFault exception: [WSDL] SOAP-ERROR: Parsing WSDL: Couldn't load from 'http://localhost/wsdl.xml' : Start tag expected, '<' not found

The issue is reproducible for HTTP and HTTPS, for local and remote websites. Also you can download the WSDL file and provide it to SoapClient as a string and in this case it works. Something wrong in getting WSDL via HTTP(S).
I googled and researched it a bit and found following:
  • I'm not first who found this issue :-)
  • There is bug in bugzilla:
  • The issue is linked to DNS stack; code starts to work if you replace DNS name by IP address  (worked for me), but that obviously won't work for name based virtual hosts
  • There is also information that issue appears for chunked encoding of HTTP response. SoapClient can't deal with it. However there is working workaround (I didn't test) by using HTTP 1.0 via stream contexts (code is given in comments of bug page linked above)
  • Another workaround linked to HTTP 1.0 is to use proxy - by default proxies do not work with HTTP 1.1 and that fixes issue:
  • In some cases it might be fixed by disabling "curlwrapper" in PHP's compilation options. It worked for me, but people complain that it doesn't work for everybody.
In general case you can disable curlwrapper by adding following parameter to configure script:
./configure --without-curlwrapper

On Gentoo Linux you can do it by disabling "curlwrapper" USE flag for ebuild dev-lang/php
It can be done by putting following line to file /etc/portage/package.use 
dev-lang/php -curlwrapper

Full list of flags which worked for me:
dev-lang/php-5.3.13  USE="apache2 berkdb bzip2 cli crypt ctype curl exif fileinfo filter gd gdbm hash iconv inifile json ldap mysql mysqli nls pdo phar pic posix readline session simplexml soap sockets spell sqlite ssl sysvipc threads tidy tokenizer truetype unicode xml xmlreader xmlrpc xmlwriter xsl zip zlib -bcmath -calendar -cdb -cgi -cjk -curlwrappers -debug -doc -embed -enchant -firebird -flatfile -fpm -frontbase -ftp -gmp -imap -intl -iodbc -ipv6 -kerberos -kolab -ldap-sasl -libedit -mhash -mssql -mysqlnd -oci8-instant-client -odbc -pcntl -postgres -qdbm -recode -sharedmem -snmp -sqlite3 -suhosin -sybase-ct -wddx -xpm"