Показ дописів із міткою linux. Показати всі дописи
Показ дописів із міткою linux. Показати всі дописи

середа, 18 серпня 2010 р.

Кубик Рубика у терміналі

Відірвався від літа на кілька годин щоб побавитися компілятором поза роботою. Ціль — емулятор кубика Рубика у терміналі. Чому в терміналі? Тому що для любителів графіки вже є готовий gnubik.

Отже, тягнемо репозитарій:

$ git clone http://github.com/sakhnik/rubik.git
Cloning into rubik...
remote: Counting objects: 134, done.
remote: Compressing objects: 100% (44/44), done.
remote: Total 134 (delta 89), reused 127 (delta 86)
Receiving objects: 100% (134/134), 20.77 KiB, done.
Resolving deltas: 100% (89/89), done.

Збираємо бінарник і маємо улюблену головоломку:

$ cd rubik
$ ./autogen.sh
$ make
$ ./src/rubik

Підтримуються такі команди (див. main.cc):

КомандаДія
qВийти
#Змішати
#NЗмішати, виконавши N поворотів
uПовернути попередній хід
uNПовернути N попередніх ходів
fПовернути передню грань за годинниковою стрілкою
FПовернути передню грань проти годинникової стрілки
fNПовернути N передню грань за годинниковою стрілкою. N=1 — передня, N=−1 — задня.
FNПовернути N передню грань проти годинникової стрілки
sNПовернути N бічну грань за годинниковою стрілкою. N=1 — ліва, N=−1 — права.
SNПовернути N бічну грань проти годинникової стрілки
tNПовернути N верхню грань за годинниковою стрілкою. N=1 — верхня, N=−1 — нижня.
TNПовернути N верхню грань проти годинникової стрілки
x,y чи zГлянути на куб з іншої сторони

Гм, треба буде цю таблицю включити в саму програму.

середа, 21 квітня 2010 р.

Про велосипеди

Мова піде про винахід колеса для кожного окремого воза. Вкотре знадобилася функція, яка б виконувала mkdir -p, тобто рекурсивно створювала директорії по заданому шляху. mkdir(2) справляється тільки тоді, коли всі батьківські директорії вже існують, а нам саме треба створити бракуючі.

Найдоступніші замінники:

  • system ("mkdir -p a/b/c/d")

    Для простої операції створює процес оболонки, а та ще один для mkdir — зажирно.

  • boost::filesystem::create_directories ("a/b/c/d")

    Цей тягне за собою не потрібну ні для чого іншого бібліотеку, жаба душить. Крім того, немає можливості задати права доступу до нових каталогів.

Ретельніший пошук показав, що у світі Unix кожен сам собі пише потрібну приблуду. А раз так, то й я не хотів зостатися осторонь. Нічого в ній особливого немає, тому прошу смакувати (C++):

        
//
// Mkdir.cc
//
//     Created: 21.04.2010
//      Author: A. Sakhnik
//

#include "Mkdir.hh"

#include <fcntl.h>
#include <errno.h>
#include <string>
#include <syslog.h>

using namespace std;

static int mkdir_if_missing (char const* path, mode_t mode)
{
    struct stat buf;
    if (-1 != ::lstat (path, &buf))
    {
        if (S_ISDIR (buf.st_mode))
            return 0;               // The path is a directory, OK
        errno = ENOTDIR;
        return -1;                  // The path exists, and isn't a directory
    }
    if (errno != ENOENT)
        return -1;                  // Something bad happened
    if (-1 == ::mkdir (path, mode))
        return -1;                  // Wasn't able to create the directory
    return 0;                       // We were lucky enough indeed
}

// Create directory recursively
int mkdir_p (char const* path, mode_t mode)
{
    string parents;                 // Partial paths a, a/b, a/b/c etc

    for (const char *next = strchr (path, '/'), *prev = path;
         next;
         prev = next, next = strchr (next + 1, '/'))
    {
        parents.insert (parents.end(), prev, next); // Consider next component
        if (parents.empty())
            continue;    // Omit root directory for absolute path
        if (-1 == mkdir_if_missing (parents.c_str(), mode))
            return -1;
    }
    return mkdir_if_missing (path, mode); // Create final component
}

// vim: set et ts=4 sw=4:

      

пʼятниця, 19 березня 2010 р.

З поверненням, Fedora

Років зо два тому почали на роботі використовувати Ubuntu, то весь цей час вона була основною оперційною системою на домашньому лаптопі. Зручна, надійна, з багатою бібліотекою прикладних програм. Але тільки побачив Fedora, мене здолала заздрість: пакунки поновлюються частіше, і всі ходові програми на один випуск новіші.

Поставив, і вже два тижні користуюся Fedora 12. Для порівняння ось два зображення одного шматка схеми USBasp в Ubuntu та новій системі:

Нарешті, надійшла пора випускати у світ спроектований стартовий годинник. Вже тиждень потію над схемою та друкованою платою. Це вже четверта плата на моєму рахунку, і цього разу вдалося щось нове: жодного перехідного отвору! Ще треба дещо додати, двічі перевірити, прогнати через DRC (перевірку правил проектування), і можна буде замовляти.

субота, 14 листопада 2009 р.

Канал через UDP

Півтора року тому для підтримки GPRS знадобилося мені реалізувати специфічний протокол SIA-over-IP. Це до возу приліпили крила й реактивний двигун, так що він тепер літає поруч із лайнерами. Тобто, сто років вже працюють у текстовому форматі SIA різні прилади, так ці самі повідомлення вже можна пропихати й через інтернет.

В документі було написано, що протокол орієнтований на з’єднання TCP, але при необхідності можна ті самі пакети слати у датаграмах UDP. Сказано — зроблено, просто для полегшення власної сверблячки зробив я обидва варіанти, хоч і ніхто не просив.

І от тижнів зо два тому телефонує інженер, який робив цей протокол для приладів, і питає, мовляв, як там у нас на сервері із UDP? Кажу, що все на місці, бери й спробуй… Працювало все нормально, але тільки до тих пір, як спробували під’єднатися до віртуального інтерфейсу. Виявилося, що пакети на сервер таки приходять, той відповідає, але вже через справжній інтерфейс. Тобто, прилад присилає на віртуальну адресу 192.168.38.14, а сервер відповідає із 192.168.38.13. Тому їх не хоче випускати у світ файрвол. Та й сам прилад, мабуть, проігнорував би.

Порившись півдня у інтернетрях, кажу архітекторові, що вся біда у прив’язці приймаючого сокета до IPADDR_ANY, давайте, мовляв, я їх заведу на кожен інтерфейс по одному, буде швидко й найдійно. — Та де там, шукай далі, як правильно обдурювати адресата, переписуючи на конверт зворотню адресу з оригіналу…

Таки надибав! Всі про це знають, але ніхто не каже вголос. Підглянув (thanks OSS!), і за день зробив. Довелося, правда, замінити recvfrom/sendto на recvmsg/sendmsg, у самому повідомленні порпатися у допоміжній інформації (struct in_pktinfo), та ще й самому її підкидати. Але так до кінця й не впевнений, що воно того було варте.

середа, 4 листопада 2009 р.

Автоматичне тестування у bash

Рік тому з’явилася була ідея створювати для автоматичної перевірки всієї системи невеликі скрипти оболонки Unix. Відверто, з нашим лідером QA ми навіть спромоглися втілити її. Після нескладної підготовки середовища, запускався написаний на bash скрипт, який по SSH керував приладом, заставляючи його відіслати якісь повідомлення. Разом із тим виконував якісь перевірки на сервері. Така нехитра схема нам дозволила автоматизувати рутинні перевірки, і зрештою довести до систему до пуття.

Тепер знову постала необхідність розкопувати старі кулемети. Розробляючи нову версію сервера з нуля, ми зіткнулися із дикою бідою. Дуже важко відслідкувати всі внесені до схеми бази даних зміни і вчасно поновлювати код демонів (хто робив, той зрозуміє). Тому тепер я намагаюся пліч-о-пліч із ручними перевірками “занотовувати” свої дії у скрипти bash. Таким чином легко потім повторювати перевірки щодня. Можливо, в майбутньому автоматизуємо нічні збірки із прогонкою всіх тестів.

Такі тести мали б називатися регресійними модульними, але вони не те саме, що модульні тести вихідного коду. Бінарний модуль — це чорна скринька, але перевіряються внутрішності системи (записи в базі даних, зміни у файловій системі тощо).

Запускаються ці диво-скрипти на перевірюваному розгорнутому сервері, але покладаючись на структуру вихідного коду. Типові приклади дій у скриптах такі:

  • Визначаються якісь вихідні значення, параметри:

    
    serial=1234567890
    msg_idx=1234
    cam_cnt=4
    

  • Готується оточення, заводяться значення в базу даних, перезапускаються потрібні демони тощо:

    
    echo "Creating database ipmp"
    mysql << END || die "Failed to recreate the database ipmp"
      DROP DATABASE IF EXISTS ipmp;
      `cat $vdns_root/documents/web/ipmp/Ipmp2.sql`
    END
    
    echo "Preparing environment"
    rm -rf /ha_shared/vdns/eventsPictures
    mysql ipmp << END || die "Failed to prepare environment"
      INSERT INTO unit
      SET utc_id = 1,
          unt_serial = '$serial',
          unt_remote_access = 'allow';
    END
    
    echo "Restarting pnet_server"
    killall -KILL pnet_server
    ($vdns_root/src/apps/pnet_server/pnet_server &)|| \
        die "Couldn't launch pnet_server"
    
    echo "Restarting pnet_notify"
    killall -KILL pnet_notify
    ($vdns_root/src/apps/pnet_notify/pnet_notify &) || \
        die "Couldn't launch pnet_notify"
    sleep 2
    

  • Від імені приладу відсилається запит:

    
    # Sending error request
    resp=`curl -s -d asdf "http://localhost:8080/scripts/notify.php"`
    [[ $? -eq 0 ]] || die "Failed to get error response"
    [[ "$resp" =~ "<index>-1</index>" ]] || die "Incorrect error response"
    
    # Sending request
    ./send1.sh -a localhost -s $serial -i $msg_idx -c $cam_cnt
    [[ $? -eq 0 ]] || die "Failed to send the event"
    

  • Нарешті, виконуються перевірки ефекту запиту:

    
    echo "Checking events"
    res=`mysql -s ipmp <<END
      SELECT etv_id,
             evt_description,
             evt_message_index,
             evt_original_device_name
      FROM event;
    END` || die "Failed to check events"
    
    diff -uw <(echo "$res") <(cat <<END
    31      Fire    $msg_idx        Control Panel
    END)
    
    [[ $? -eq 0 ]] || die "Event don't match"
    

Для порівняння табличних даних в нагоді дуже стає команда diff: виходить компактно, інформативно. Краса!