Tag: linux

  • Back to MySQL

    不知从什么时候开始,Fedora 和 CentOS 里的 MySQL 都给换成了 MariaDB。我一直搞不懂好端端的 MySQL 为什么还要分出一个 MariaDB,更搞不懂 MariaDB 的优越性在哪里,反正 MySQL 对我来说够用了,倒是 MariaDB 安装和启动时使用了不同于 MySQL 的名称,让我很不习惯。比如

    yum install mysql-server
    

    换成了

    yum install mariadb
    
    service mysqld start
    

    换成了

    service mariadb start
    

    幸好,除此之外,一切照旧,用到 MySQL 的 PHP 程序都能继续在 MariaDB 使用,所以我就一直用着 MariaDB。直到今天使用 Magento2,要求 MySQL 5.6 以上版本,而 CentOS 下把 MariaDB 用 yum update 到最新版本,仍是 5.5.40。

    于是我被逼着去下载 MySQL 5.6。不用不知道,一用发现 CentOS 下安装 MySQL 超级方便,只要安装好 RPM 包——http://dev.mysql.com/get/mysql-community-release-el7-5.noarch.rpm,就可以恢复使用

    yum install mysql-server
    

    执行安装。

    早知如此,我真不该碰 MariaDB——这个并没有给我带来实际利益的程序也花了我一天去熟悉。

  • Change Grub timeout seconds

    Grub menu
    Grub menu

    Changing Grub timeout seconds is not as many people said, by edit /etc/default/grub. At least with Fedora 20, I can proove it is not the right place to make the change. The corresponding file is /boot/grub2/grub.cfg. In the file look for

    set timeout=5
    

    5 seconds is the default timeout. Change it to any seconds you want it to be.

    set timeout=0
    

    0 seconds means no waiting at all.

    set timeout=-1
    

    -1 means wait infinite seconds until you make a choice.

    (My updated knowledge on 06 Feb 2014) /boot/grub2/grub.cfg 是针对 BIOS systems 的,UEFI systems 要修改 /boot/efi/EFI/fedora/grub.cfg。修改 grub.cfg 文件后直接生效,但是不建议直接修改 grub.cfg 文件。grub.cfg 的文件头上就有一段这样的提示:

    #
    # DO NOT EDIT THIS FILE
    #
    # It is automatically generated by grub2-mkconfig using templates
    # from /etc/grub.d and settings from /etc/default/grub
    #

    因为系统在某些情况下会覆写 grub.cfg 文件。这样又回到之前很多人说的 /etc/default/grub 文件,在这里修改参数是对的,只是修改后不直接生效,还要运行一遍

    grub2-mkconfig -o /boot/grub2/grub.cfg
    
    

    grub2-mkconfig -o /boot/efi/EFI/fedora/grub.cfg
    
    

    覆写 grub.cfg 才生效。

  • Machine convert xls to csv

    Linux is an excellent OS. Today something again proved my choice.

    I was looking for a solution to convert Excel xls to csv format. This conversion is required by a Magento dataflow project.

    Magento native dataflow comes with an XML Excel parser. For some reasons, it does not convert my xls files. I think it may be due to limited functionality of Magento XML Excel parser class, or encoding problem.

    I need something which can reliably convert xls files from 3rd parties, which we have no control over how they produce xls files.

    I was thinking of another php independent class called ExcelReader. However, ExcelReader goes beyond what I need. All I need is convert xls to csv, make ready for dataflow.

    After a day’s research, finally xls2csv caught my attention. I would have found it earlier if I had searched by “linux command line convert xls to csv” earlier. It runs perfectly well on Fedora and CentOS as I tested.

    Installation is straight forward. Run

    
    yum install catdoc
    
    

    FYI, yum install xls2csv, installs something totally different. I have not figured out what it is.

  • Learned something basic

    今天又长知识了。

    首先,最简单的,更正了长久以来的想当然,php 下 explode(‘,’, ”) != array(),而是得到长度为 1 的数组,key 是数值 0,value 是空字符串。天哪,我有多少个程序是基于 explode(‘,’, ”) == array() 写下去的,这下影响大了,得好好查一查。

    其次,发现一个不晓得是 sshfs 的 bug 还是 gedit 的 bug。复制错误的过程是:用 Nautilus 或 Dolphin 打开 sshfs 挂载的目录,右击创建一个新文件。文件创建是成功的,属性是 774,用 gedit 打开它却无法保存,提示是没有写权限;但用 kwrite 编辑保存一切正常;用 gedit 再编辑 kwrite 编辑过的文件又能保存。或者,在右击创建一个新文件后,执行一次 chmod 774 filename,也能用 gedit 编辑保存了。

    再次,发现在 IE6 下,用 javascript 增大元素的尺寸(比如 jQuery widget 化,增加 border,增加 padding,等),会增大父元素的尺寸。哪怕父元素已用 css 静态赋以宽度值,宽度也会被改变,这是某些精心布局在标准浏览器下很好看,到了 IE6 就面目全非的一大原因。万恶的 IE6 啊,当然从另一方面,说明精心布局仍不够“精心”。为 IE6 布局好比极限运动,挑战好心情的极限;如果看到下属多花一倍时间 fix for IE6,挑战的也是老板的心理极限。

    然后,发现 jQuery gallery 里有两个 themes (Humanity, Vader) 的参数不太正常,多了 tr 参数,不知道怎么多出来的,删了似乎没影响。

    最后,如果父元素包含所有的子元素都是 float:left 或 float right 的话,不做特殊操作,父元素是没有高度的。父元素的后续元素用一个 clear:both 就能站到该站的位置,但如要为父元素本身画一个边框就稍有难度。最早我用的办法是在这个元素所有的子元素之后增加一个隐形的<div style=”height:0; clear:both;”></div>,但这个硬生生加进去的元素改变了 DOM 结构,破坏了语义,不够 SEO。在 Magento 里学到了另一个方法 :after { clear:both; },如 .clearer 的示例,但要为低版本的 IE 专门写 clear after fix。我维护一份自己的 style.css,override Magento 原版的 style.css。我觉得这个任务就很“繁重”,如果再来一份自己的 style-ie.css,override Magento 原版的 style-ie.css,就为 clear after fix?总觉得小题大作。今天发现一个 clear after all floating children 的 neat solution,就是在父元素上设定 style=”overflow:auto; zoom:1″。overflow 让父元素调整到应有的高度,zoom 也是必须的,否则 IE6…,唉!

    最后的最后,发现 z-index 值在 IE6 下被重置的简单通用的 fix。这个问题的来源是若干个 position 后的元素,给它们设定 z-index,IE6 下根本不按设定值 layout,而且还摸不到规律。比如下拉菜单,有时被其他东西给压住。fix 是赋予下拉菜单 z-index 时,赋予父元素(整个菜单)更高的 z-index。

  • Solve media type differencing after VirtualBox upgrade to 3.2.8

    Virtualbox Disks Inaccessible
    Virtualbox Disks Inaccessible

    我在 VirtualBox 升级到 3.2.8 以后发现其中的 Windows XP Guest OS 无法启动,其他 Guest OS 正常。Windows XP Guest OS 用到的两个 harddisk 文件也显示 inaccessible,而且也 release 不了它们,强行访问还给一个错误信息。

    Medium type of ‘/path/to/harddiskfile’ is differencing but it is not associated with any parent medium in the media registry (‘/path/to/.VirtualBox/VirtualBox.xml’).

    我也没怎么上心,觉得是 VirtualBox 升级版中的 bug,指望在后续版本中把它修复。可是好久没见 Oracle 出更新,只好 google 求帮助。

    我发现跟我有同样遭遇的人还不少,原因或许是 Host OS 使用了 ext4 文件系统导致不兼容。可为什么只影响 Windows Guest OS?管不了那么多,找解决办法要紧。

    解决方案众说纷纭。最后真正解决我的问题的是:

    Step 1: 去下载一个 fix 工具。我下载了 for linux VBoxFixHdd-Linux.tar.gz,其他的去 http://www.virtualbox.org/download/VBoxFixHdd/ 找。

    Step 2: 运行一下
    VBoxFixHdd --filename /path/to/image.vdi --zeroparentuuid

    其中 /path/to/image.vdi 就是 access 不了、release 不掉的问题文件,当然后缀不一定是 vdi,我的是 vmdk(不知道还有别的吗)。

  • Ftp user chroot setting on CentOS

    在 CentOS 上,怎么把 linux user ftp 登录后限制在它的 home directory?很久以前我设置过一次,知道是很简单的一个设置,今天需要同样的事情再做一次,竟然怎么也想不起来。要命的是,google 了好几个小时都不得要领。

    可以说半天时间是浪费掉的。最后当然是重新找到了办法,赶紧记下来:

    Edit /etc/proftpd.conf, add

    DefaultRoot ~

    当然前提预装有 proftpd。如果还预装 plesk,则更简单,/etc/proftpd.conf 已含有

    DefaultRoot ~ psacln

    只要把新建的用户归入 secondary group psacln 即可。

  • Clean temporary files left by editors

    打开文件进行编辑或其他操作后,有时会留下一些临时文件。按理只有非正常中止会留下临时文件,但有时即使正常退出仍在硬盘上留下一堆垃圾,可能某些编辑器有 bug。

    不过,清理临时文件也不费力,找准文件名特征可以一次性删除它们。

    find /var/www -name ".goutputstream-*" -exec rm -rf {} \;

    据说 .goutputstream-* 是 GNOME 拷贝粘帖文件时的临时文件。

    find /var/www -name ".tmp*~" -exec rm -rf {} \;

    .tmp*~ 是 Text Editor 编辑时的临时文件。

    保持良好的习惯,不要用 root 身份去执行此类操作,执行前也要再三检查查找起始位置是否正确。

  • A tool to synchronise Magento database between servers

    我时不时需要在测试服务器上加载生产服务器的实时数据,以前都是把数据下载到本地的测试服务器后,手工键入一些命令完成数据加载,每次都要花费几分钟时间。为了避免一再“浪费”这几分钟,我今天一次性投入了几小时完成了一个 php 脚本。虽然这是为 magento 的数据迁移而写的脚本,但我写完一看,用在其他地方也是可以的。

    为了安全起见,该脚本是用 php 命令行运行的,所有输出针对 terminal 美化,不是 browser。保存源码为 data_mover.php,同一目录下要有 mysqldump 得到的经 gzip 的 sql 文件,文件名以 FILENAME_PREFIX 开头,以 .sql.gz 结尾。启动时只需键入

    /path/to/php -f data_mover.php

    即可。

    初始化 PDO 对象时,按理只需要 host=localhost,不需要 unix_socket=MYSQL_SOCKET。但奇怪的是,如果通过 apache 调用本程序(虽然不是本程序的初衷,但我希望它在浏览器下也能运行),仅指定 host=localhost 作 PDO __construct() 参数,会产生一个莫名其妙的错误:

    SQLSTATE[HY000] [2002] Can’t connect to local MySQL server through socket ‘/tmp/mysql.sock’ (2)

    似乎是 PDO bug。可以通过 host=127.0.0.1 或者追加 unix_socket=MYSQL_SOCKET 来避免。如果通过 php 命令行启动则没有这个问题。

    而后,还涉及怎么删除所有数据表的问题。看似简单的一个问题,我发现 mysql 竟然没有一个类似于 DROP/TRUNCATE TABLE * 单行命令。于是除了本脚本用的方法外,我还想了不下两种办法:

    其一,删掉整个数据库重新创建。
    mysqladmin -f -h localhost -u (DB_ROOT_USERNAME) -p(DB_ROOT_PASSWORD) drop (DB_NAME)
    mysqladmin -h localhost -u (DB_ROOT_USERNAME) -p(DB_ROOT_PASSWORD) create (DB_NAME)
    但这需要比数据表操作更高权限的用户,在这个无关大局的脚本里去使用高权限的用户的密码,实在非我所愿。

    其二,是我 google 来的,方法很巧,但很可惜,因为 foreign keys 的存在,运行这条命令会出错。
    mysqldump -u (DB_USERNAME) -p(DB_PASSWORD) –add-drop-table –no-data (DB_NAME) | grep ^DROP | mysql -u (DB_USERNAME) -p(DB_PASSWORD) (DB_NAME)

    
    <?php
    
    define('MYSQL_SOCKET', '/path/to/mysql/socket');
    define('DB_NAME', 'db_name');
    define('DB_USERNAME', 'db_username');
    define('DB_PASSWORD', 'db_password');
    define('FILENAME_PREFIX', 'filename_prefix');
    define('TEST_URL', 'http://test.domain/');
    
    if ($handle = opendir(dirname(__FILE__))) {
    $found = false;
    /* This is the correct way to loop over the directory. */
    while (false !== ($file = readdir($handle))) {
    if (substr($file, 0, 12) == FILENAME_PREFIX && substr($file, -7) == '.sql.gz') {
    if ($found) {
    //compare which one is newer
    if (filemtime($file) > filemtime($fileFound)) {
    $fileFound = $file;
    }
    }
    else {
    $found = true;
    $fileFound = $file;
    }
    }
    }
    
    if ($found) {
    echo "Found the newest file $fileFound, and will work on it.\n";
    try {
    $pdo = new PDO(
    'mysql:host=localhost;dbname=' . DB_NAME . ';unix_socket=' . MYSQL_SOCKET,
    DB_USERNAME,
    DB_PASSWORD,
    array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8")
    );
    echo "Connected to database.\n";
    }
    catch (Exception $e) {
    die ("Error occurred when connecting to database. Quoting error message: " . $e->getMessage() . "\n");
    }
    $sql = 'SET FOREIGN_KEY_CHECKS = 0';
    $pdo->prepare($sql)->execute();
    
    /* query all tables */
    $sql = "SHOW TABLES FROM ". DB_NAME;
    $stmt = $pdo->prepare($sql);
    $stmt->execute();
    $result = $stmtDale->fetchAll(PDO::FETCH_COLUMN, 0);
    $magentoTableNames = array();
    /* add table name to array */
    foreach ($result as $tableName) {
    if (substr($tableName, 0, 8) == 'magento_') {
    $magentoTableNames[] = $tableName;
    }
    }
    
    //drop all magento tables
    $count = count($magentoTableNames);
    if ($count > 0) {
    $sql = 'DROP TABLE '. implode(',', $magentoTableNames);
    $pdo->prepare($sql)->execute();
    echo "Found and dropped $count magento tables.\n";
    }
    else {
    echo "No existing magento tables found.\n";
    }
    
    //import data via pipe
    echo "Importing data. It may take a while...\n";
    $output = shell_exec("gunzip < $fileFound | mysql -h localhost -u " . DB_USERNAME . " -p" . DB_PASSWORD . " " . DB_NAME);
    echo "Importing data completed.\n";
    if ($output) {
    echo "This is output during data import:\n$output\n";
    }
    
    //after import, change some data for test domains
    $sql = 'UPDATE magento_core_config_data SET value=? WHERE scope=? AND scope_id=? AND path=?';
    $stmt = $pdo->prepare($sql);
    $stmt->execute(array(TEST_URL, 'default', 0, 'web/unsecure/base_url'));
    $stmt->execute(array(TEST_URL, 'default', 0, 'web/secure/base_url'));
    $stmt->execute(array(TEST_URL, 'websites', 2, 'web/unsecure/base_url'));
    $stmt->execute(array(TEST_URL, 'websites', 2, 'web/secure/base_url'));
    echo "Config data changed to fit testing environment.\n";
    echo "All missions completed.\n";
    }
    else {
    echo "File not found. What else can I do?\n";
    }
    closedir($handle);
    }
    else {
    echo "File access not permitted.\n";
    }
    
    
  • How can Magento generate sitemap.xml belongs to root?

    今天看到 magento 目录下的 sitemap.xml 的属主和属组分别是 root:root,属性是644,而这一台的 webserver 是以 apache:apache 身份运行的,所以我觉得很奇怪:

    • apache 怎么能建立一个 root:root 的文件?
    • 如果这个文件不是 apache 建的,apache 怎么有权去更新它(最近更新就在今天)?

    想了好一会明白过来,sitemap.xml 是 cron job 建的,crontab 这么写

    */10 * * * * /usr/bin/php -f /path/to/magento/cron.php

    那么 sitemap.xml 归 root:root 所有就不奇怪了。

    我想这样写会好些:

    */10 * * * * sudo -u apache -g apache /usr/bin/php -f /path/to/magento/cron.php

    2010年6月29日更新:上行命令不对,设想在 cron job 里以 apache 身份运行 cron.php,但出错。正确的写法应该是:

    */10 * * * * su -c '/usr/bin/php -f /path/to/magento/cron.php' -s /bin/bash apache

  • Nautilus mount

    Nautilus is quite good, easy to use in regards to its mount feature. It can automatically “mount” Windows Share via smb:// protocol. However, some software, e.g. FileZilla (now 3.2.4.1) can not read from or write to smb:// protocol. A genuine Linux mount is more helpful than Nautilus mount.