воскресенье, 31 января 2010 г.

Оборудование для систем спутникового слежения

Piligrim представила системы спутникового слежения размером со спичечный коробок

Миниатюрные устройства с заявленными прекрасными техническими характеристиками и разумной ценой - выглядит неплохо. Бесплатный сервер сбора информации по GPRS - отличный маркетинговый ход. Вот интересно, как насчет возможности подключения внешних датчиков.

Upd. Посмотрел инфу от производителя (питерская контора) - реализация и сервис примитивные, координаты пересылает на сервер по HTTP при поступлении входящего звонка (сам звонок не принимает, т.е. платить за него не придется). Непонятно, можно ли настроить регулярную передачу координат. Датчики подключать нельзя. Форум поддержки лучше не читать, ибо полная лажа, имхо похоже на то, что все посты созданы непосредственно сотрудниками производителя. Если будет время, постараюсь посмотреть аналоги за рубежом, т.к. устройство сделано явно на нескольких готовых компонентах, а себестоимость устройства, думаю, где-то в районе 50 евро.

пятница, 29 января 2010 г.

e-mail адреса с ограниченным временем жизни с qmail

Красивое решение для блокирования спама с помощью суффиксов имени пользователя:

a simple way is to create ~phil/.qmail-default containing

|if test "$EXT" '>' `date +%Y%m%d`; then echo address expired; exit 100; fi

followed by delivery instructions for the successful case. You may, of
course, choose to replace the simple one-liner with a script checking
that $EXT really has the form of a date, etc.

I used something like this a long time ago, and then I used these
addresses for my usenet postings, thinking that any spam generated
for these addresses would sooner or later get stopped. It wasn't a
great success, and it ended up pissing off a friend when he tried to
respond to a usenet posting of mine a couple months after I had posted
it. But spam for these addresses, including garbled versions including
only the date, kept coming for years after I stopped doing this.


Оригинал см. по следующему адресу Temporary email addresses Тут же можно посмотреть и другие реализации этой идеи.

Грейлистинг и антиспам для qmail

В продолжение темы об использовании qmail в debian-russian решил сохранить ссылочку, иначе в следующий раз придется снова разыскивать:
Greylisting Links

А вот по этой ссылке можно посмотреть описание более мощного решения:
latest rblsmtp filter

Также рекомендую заглянуть по следующему адресу: qmail Information
См. патчи, ссылки, HOWTO.

И напомню заодно адрес неофициального сайта поддержки, так как ранее я приводил только адрес русскоязычного зеркала.

В рассылке qmail встретил тему, созданную мантейнером неофициального дебиан-пакета с qmail, а так как этот вопрос я слышу с завидной регулярностью, привожу ссылку: netqmail: reject unknown recipients during SMTP В частности, интересна следующая цитата:

from qmail.org:
- Andrew Richards has modified Paul Jarc's realrcptto into qmail-verify.
It now uses UDP for privilege separation which also allows an incoming
mail server to query a separate mailstore for larger installations.
http://free.acrconsulting.co.uk/email/qmail-verify.html

среда, 27 января 2010 г.

Баллада о qmail

В debian-russian завертелась дискуссия о qmail и других MTA. Не скрою, свою руку я к этому очень даже приложил. Хотелось бы кратко резюмировать - интерес к qmail зачастую спотыкается на том, что мол нет актуального пакета в дебиане (напомню, место действия - рассылка дебиана) и никто тот qmail не поддерживает и как бы не остаться с ним в гордом одиночестве. Сначала я полагал, что мой собеседник специально придирается к мелочам, вместо того, чтобы "зрить в корень":

Чтоб мудро жизнь прожить, знать надобно немало,
Два важных правила запомни для начала:
Ты лучше голодай, чем что попало есть,
И лучше будь один, чем вместе с кем попало.


Но, признаюсь, я был не прав, и стоило предоставить оппоненту больше информации к размышлению. Итак, неофициальный репозиторий sid/lenny с утилитами DJB от замечательного мантейнера Gerrit Pape (он же, кстати, поддерживает пакет dash). Заметим, что его сборка qmail давным-давно загружена на ftp-master, но и по сей день там "маринуется".

Также рекомендую обратиться к дискуссии о qmail между debian-developers.

Надеюсь, для сомневающихся мой пост поможет убедиться, что qmail в порядке и развивается, а временная задержка (долгая, правда) с обновлением дебиан-пакета может быть компенсирована использованием репозитория одного из известных мантейнеров.

вторник, 26 января 2010 г.

Производительность SQLite FTS3

Возвращаясь к вопросу о производительности модуля полнотекстового поиска в SQLite, мы можем наблюдать весьма приятную картину:


$ ls -lh|grep test.db|awk '{print $5}'
15G

$ sqlite3 test.db
SQLite version 3.6.21

sqlite> select count(*) from file_text;
908800
sqlite> select total(length(content)) from file_text;
6552194816.0
sqlite> select count(docid) from file_text where file_text match 'претензия';
1536
CPU Time: user 0.004001 sys 0.000000
sqlite> select count(docid) from file_text where file_text match 'новости август абонент';
256
CPU Time: user 0.028002 sys 0.000000
sqlite> select count(docid) from file_text where file_text match 'новости август абонент*';
512
CPU Time: user 0.420026 sys 0.000000


Резюме: набор из почти миллиона документов общим размером 100 Гб, суммарно содержащих 6,5 миллиардов знаков, хранится в 15 Гб БД и поиск по одному слову занимает единицы миллисекунд, по нескольким словам - десятки миллисекунд, по нескольким словам с маской - сотни миллисекунд. Черт возьми, ведь это уже и есть светлое будущее.

Помогите защитить Байкал!

> Друзья, коллеги!
> > > Пересылаю письмо о Байкале.
> > > Прошу Вас подписать обращение и распространить письмо, как можно шире.
> > >
> > >
> > > С уважением,
> > >
> > > Валерий Шаров
> > >
> > >
> > >
> > > Друзья!
>
> > > Правительство России совершает большую ошибку!
> > > Первое постановление 2010 года Владимир Путин направил не на решение
> > > нарастающих проблем нашего народа, наших детей и стариков, а на спасение
> > > своего давнего компаньона - олигарха Дерипаски.
> > > Преступным постановлением Путин разрешил беспринципному и наглому
> > > Дерипаске
> > > сливать отходы от своего БЦБК в наш Байкал!
> > > Помогите отменить это позорное решение. Подпишите обращение к Президенту
> > > Д.Медведеву: http://babr.ru/baikal/
> > > Отправьте, пожалуйста, это сообщение всем друзьям всеми способами -
> > > e-mail, ICQ, SMS. Мы не можем оставить наш Байкал без поддержки!
> > > С уважением
> > > Байкальское движение
> > > Кафедра экологии Восточно-сибирской академии образования

суббота, 23 января 2010 г.

Репозиторий с apt-ftparchive

Заменяя reprepro на apt-ftparchive, в первую очередь хотелось сохранить имеющуюся структуру каталогов. Нижеприведенная конфигурация решает эту задачу, только каталог pool разделен на pool-lenny и pool-etch, поскольку так проще поддерживать.

Вот такая команда обновит список пакетов локального репозитория:
/etc/apt/apt-archive-mbg

А такая команда обновит репозиторий на основном сервере:
apt-archive-mbg-upload
Замечу, что секция non-free из локального репозитория будет скопирована в отдельный закрытый репозиторий.

Ниже приведены скрипты и конфиги, необходимые для решения указанной задачи.
apt-archive-mbg-upload
#!/bin/dash

/usr/bin/rdiff-backup --exclude '**/main' /mnt/work/apt-archive-mbg .../apt-archive-mbg
/usr/bin/rdiff-backup --exclude '**/non-free' /mnt/work/apt-archive-mbg .../debian

/etc/apt/apt-archive-mbg
#!/bin/dash

apt-ftparchive generate /etc/apt/apt-archive-mbg.conf 

cd /mnt/work/apt-archive-mbg/dists/etch
apt-ftparchive -c /etc/apt/apt-mbg-etch.conf release . > Release
rm -f Release.gpg
gpg --output Release.gpg -ba Release

cd /mnt/work/apt-archive-mbg/dists/lenny
apt-ftparchive -c /etc/apt/apt-mbg-lenny.conf release . > Release
rm -f Release.gpg
gpg --output Release.gpg -ba Release

/etc/apt/apt-ftparchive-public.conf
Dir {
ArchiveDir "/mnt/work/apt-public";
CacheDir "/mnt/work/apt-public";
};

Default {
Packages::Compress ". gzip bzip2";
Sources::Compress ". gzip bzip2";
Contents::Compress ". gzip bzip2";
};

TreeDefault {
BinCacheDB "packages-$(DIST)-$(SECTION)-$(ARCH).db";
Directory "pool-$(DIST)/$(SECTION)";
Packages "dists/$(DIST)/$(SECTION)/binary-$(ARCH)/Packages";
SrcDirectory "pool-$(DIST)/$(SECTION)";
Sources "dists/$(DIST)/$(SECTION)/source/Sources";
Contents "dists/$(DIST)/Contents-$(ARCH)";
};

Tree "etch" {
Sections "main";
Architectures "i386 source";
}
Tree "lenny" {
Sections "main non-free";
Architectures "i386 source";
}

/etc/apt/apt-public-etch.conf
APT::FTPArchive::Release::Origin "MBG";
APT::FTPArchive::Release::Label "MBG public";
APT::FTPArchive::Release::Suite "oldstable";
APT::FTPArchive::Release::Codename "etch";
APT::FTPArchive::Release::Architectures "i386 source";
APT::FTPArchive::Release::Components "main";
APT::FTPArchive::Release::Description " MBG Public APT Repository";

/etc/apt/apt-public-lenny.conf
APT::FTPArchive::Release::Origin "MBG";
APT::FTPArchive::Release::Label "MBG public";
APT::FTPArchive::Release::Suite "stable";
APT::FTPArchive::Release::Codename "lenny";
APT::FTPArchive::Release::Architectures "i386 source";
APT::FTPArchive::Release::Components "main non-free";
APT::FTPArchive::Release::Description " MBG Public APT Repository";


Теперь можно держать в репозитории несколько версий каждого пакета, что часто бывает необходимо в процессе разработки.

Upd.
Выложил конфиги для управлениями моим репозиторием здесь же: http://mobigroup.ru/debian/ Скрипт apt-archive-mbg обновляет репозитории для всех требуемых релизов (запускать после добавления/удаления пакетов в репозиторий). Обновление инкрементальное, так что при большом количестве пакетов работает вполне шустро (моя рабочая машинка нынче - нетбук, так что я знаю толк в шустрости - мне его ресурсов хватает). На сервер пересылаю копию локального репозитория скриптом apt-archive-mbg-upload:
/usr/bin/rdiff-backup --exclude './apt-archive-mbg-upload' --exclude './*db' --exclude '**/non-free' . [путь на сервере к открытому репозиторию]
/usr/bin/rdiff-backup --exclude './apt-archive-mbg-upload' --exclude './*db' --exclude '**/main'     . [путь на сервере к закрытому репозиторию]
Скрипт apt-archive-mbg-upload содержит пути на сервере, потому его не выкладываю.

четверг, 21 января 2010 г.

Язык newLISP

В поисках средства для создания быстро стартующих утилит провел тесты и среди реализаций lisp. Самым быстрым оказался newLISP. Поставил deb-пакет с офсайта, разумеется, с поддержкой unicode. Поскольку у него в комплекте предоставляется и модуль для работы с SQLite, то я сразу же написал соответствующий тест. Ниже приведено сравнение с D-версией:

$ time newlisp ./sqlite.lsp
3.6.21

real 0m0.013s
user 0m0.008s
sys 0m0.004s

$ time ./sqlite
DB open.
sqlite_version() = 3.6.21
DB closed.

real 0m0.010s
user 0m0.004s
sys 0m0.004s

$ cat sqlite.lsp
(module "sqlite3.lsp")
(if (sql3:open "lsp.db") (sql3:error))
(println (((sql3:sql "select sqlite_version()") 0) 0))
(sql3:error)
(sql3:close)
(exit)

$ cat sqlite.d
import sqlite3;

int main()
{
sqlite3* db;
int code;
sqlite3_stmt *stmt;
char *sql="select sqlite_version();";

code = sqlite3_open("file.db", &db);
if(SQLITE_OK != code)
{
printf("DB create error: %s\n", sqlite3_errmsg(db));
return 1;
}
printf("DB open.\n");

int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, cast(char**)0);
if( rc ){
printf("SQL error: %d : %s\n", rc, sqlite3_errmsg(db));
}else{
rc = sqlite3_step(stmt);
if( SQLITE_ROW == rc ) {
printf("%s = %s\n", sqlite3_column_name(stmt,0),
sqlite3_column_text(stmt,0));
}
rc = sqlite3_finalize(stmt);
}
if (rc != SQLITE_OK) {
printf("SQL error: %d : %s\n", rc, sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
sqlite3_close(db);
printf("DB closed.\n");

return 0;
}

Как видим, скорость выполнения newLisp-скрипта лишь незначительно отстает от компилированного кода. Для программ, написанных на C и D скорость выполнения практически неотличима, а программа на D осталась от предыдущего теста, так что ее и взял за образец.
Примеры работы с SQLite можно посмотреть здесь:

$ less /usr/share/newlisp/modules/sqlite3.lsp


А теперь посмотрим, что мы можем получить на ноутбуке с процессором CoreDuo и с текущей частотой 800МГц:

$ /usr/bin/tcpserver -c 5 -- 0 8888 newlisp sqlite.lsp
$ openload localhost:8888 10
URL: http://localhost:8888/
Clients: 10
MaTps 232.77, Tps 232.77, Resp Time 0.042, Err 100%, Count 233
MaTps 234.64, Tps 251.50, Resp Time 0.040, Err 100%, Count 485
MaTps 236.58, Tps 254.00, Resp Time 0.039, Err 100%, Count 739
MaTps 238.36, Tps 254.46, Resp Time 0.039, Err 100%, Count 996
MaTps 240.30, Tps 257.74, Resp Time 0.039, Err 100%, Count 1254

$ /usr/bin/tcpserver -c 5 -- 0 8888 ./sqlite
$ openload localhost:8888 10
URL: http://localhost:8888/
Clients: 10
MaTps 345.00, Tps 345.00, Resp Time 0.029, Err 100%, Count 345
MaTps 345.70, Tps 352.00, Resp Time 0.028, Err 100%, Count 697
MaTps 346.06, Tps 349.30, Resp Time 0.029, Err 100%, Count 1047
MaTps 346.45, Tps 349.95, Resp Time 0.029, Err 100%, Count 1398
MaTps 346.66, Tps 348.61, Resp Time 0.029, Err 100%, Count 1748

Разница в 40% для скрипта и бинаря мне кажется просто фантастическим результатом. Очевидно, что для небольших приложений newLISP окажется прекрасным выбором.

По непонятным мне причинам функция onecolumn в баблиотеке sqlite3.lsp не объявлена, но это дело поправимое:

(module "sqlite3.lsp")
(define (sql3:onecolumn sql-str sql-args) ((sql3:sql sql-str sql-args) 0 0))
(if (sql3:open "lsp.db") (sql3:error))
(println (sql3:onecolumn "select sqlite_version()"))
(sql3:error)
(sql3:close)
(exit)


Скриптовый язык newLISP

среда, 20 января 2010 г.

Язык D

В поисках С-подобного языка с нативной поддержкой строк решил попробовать язык D. Цель состоит в оптимизации строковых операций в небольших сишных утилитах, в остальном С меня вполне устраивает.

Быстродействие циклов и арифметики оказывается полностью идентично сишному варианту:

$ cat hello.d
int main(char[][] args){
int i;
double x;
for (i=0,x=0;i<100000000;i++)
x+=1.1*i;
printf("i=%d; x=%.2f\n",i,x);
return 0;
}

$ gdc-4.1 hello.d -o hello
$ time ./hello
i=100000000; x=5499999945475522.00

real 0m0.535s
user 0m0.528s
sys 0m0.000s


$ cat hello.c
#include "stdio.h"
main(){
int i;
double x;
for (i=0,x=0;i<100000000;i++)
x+=1.1*i;
printf("i=%d; x=%.2f\n",i,x);
}

$ gcc hello.c -o hello
$ time ./hello
i=100000000; x=5499999945475522.00

real 0m0.563s
user 0m0.524s
sys 0m0.004s

А теперь рассмотрим более жизненный пример, на примере работы с SQLite базой данных. Элементарный пример взят отсюда, плюс добавлен код для выполнения sql-запроса.

$cat sqlite.d
import sqlite3;

int main()
{
sqlite3* db;
int code;
sqlite3_stmt *stmt;
char *sql="select sqlite_version();";

code = sqlite3_open("file.db", &db);
if(SQLITE_OK != code)
{
printf("DB create error: %s\n", sqlite3_errmsg(db));
return 1;
}
printf("DB open.\n");

int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, cast(char**)0);
if( rc ){
printf("SQL error: %d : %s\n", rc, sqlite3_errmsg(db));
}else{
rc = sqlite3_step(stmt);
if( SQLITE_ROW == rc ) {
printf("%s = %s\n", sqlite3_column_name(stmt,0),.
sqlite3_column_text(stmt,0));
}
rc = sqlite3_finalize(stmt);
}
if (rc != SQLITE_OK) {
printf("SQL error: %d : %s\n", rc, sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
sqlite3_close(db);
printf("DB closed.\n");

return 0;
}

$ gdc-4.1 -lsqlite3 sqlite.d -o sqlite
$ time ./sqlite
DB open.
sqlite_version() = 3.6.21
DB closed.

real 0m0.010s
user 0m0.004s
sys 0m0.004s

Замечу, что SQLite у меня собран с libICU, а в более простой конфигурации быстродействие будет выше.

Необходимый для сборки sqlite3.d был найден здесь:
http://prowiki.org/wiki4d/wiki.cgi?DatabaseBindings Указана версия SQLite 3.5.1, но у меня без проблем заработал с версией 3.6.21 моей сборки. Оно и правильно, поскольку SQLite API стабилен и обеспечивает обратную совместимость, так что только добавились некоторые новые функции, которые нас пока не интересуют.

Есть две реализации D, см.
http://www.digitalmars.com
D Programming Language

Язык Lua

Шелл lua запускается довольно шустро, решил еще некоторые тесты провести. Итак, встраиваем интерпретатор в сишную программу.

Скрипт читается с stdin

/*
Compile as
gcc -o2 -I/usr/include/lua5.1/ -llua5.1 lua.c -o lua

Use as
echo -e "a = 1 + 1;\nprint( a);\n"|./lua
*/
#include <stdio.h>
#include <string.h>
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>

int main (void) {
char buff[256];
int error;
lua_State *L = lua_open();
luaL_openlibs(L);

while (fgets(buff, sizeof(buff), stdin) != NULL) {
error = luaL_loadbuffer(L, buff, strlen(buff), "line") || lua_pcall(L, 0, 0, 0);
if (error) {
fprintf(stderr, "%s", lua_tostring(L, -1));
lua_pop(L, 1); // pop error message from the stack
}
}

lua_close(L);
return 0;
}

Скрипт вкомпилирован в тело программы

/*
Compile as
gcc -o2 -I/usr/include/lua5.1/ -llua5.1 lua.c -o lua

Use as
./lua
*/
#include <stdio.h>
#include <string.h>
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>

int main (void) {
int error;
lua_State *L = lua_open();
luaL_openlibs(L);

char* buff = "a = 1 + 1;\nprint( a);\n";
error = luaL_loadbuffer(L, buff, strlen(buff), "line") || lua_pcall(L, 0, 0, 0);
if (error) {
fprintf(stderr, "%s", lua_tostring(L, -1));
lua_pop(L, 1); // pop error message from the stack
}

lua_close(L);
return 0;
}


Что ж, работает. А теперь посмотрим, что же можно на этом луа делать. Например, обращение к SQLite базе данных:


require "luasql.sqlite3"

db = sqlite3.open("example-db.sqlite3")

db:exec[[ CREATE TABLE test (id, content) ]]

stmt = db:prepare[[ INSERT INTO test VALUES (:key, :value) ]]

stmt:bind{ key = 1, value = "Hello World" }:exec()
stmt:bind{ key = 2, value = "Hello Lua" }:exec()
stmt:bind{ key = 3, value = "Hello Sqlite3" }:exec()

for row in db:rows("SELECT * FROM test") do
print(row.id, row.content)
end


А вот это уже не работает. Сильно напоминает питоновские проблемы с несовместимостью минорных версий интерпретатора с библиотеками. Резюме - обойдусь без lua.

Upd.
В debian-russian подсказали вот такой вариант:

require "luasql.sqlite3"
env = luasql.sqlite3()
db=env:connect("example-db.sqlite3")
db:execute[[ CREATE TABLE test (id, content) ]]

Оно работает, но писать тест на луа уже неинтересно.

вторник, 19 января 2010 г.

Сумма прописью на tcl

По словам Anton Kovalenko, "КЛАДР напомнил о бурной юности..", и он прислал мне кусок своего кода, любезно разрешив его опубликовать на правах Public Domain:

namespace eval speller {
set names(19) {
{ноль} {один одна} {два две} три четыре пять шесть семь восемь девять
десять одиннадцать двенадцать тринадцать четырнадцать пятнадцать
шестнадцать семнадцать восемнадцать девятнадцать}
set names(90) { {} {}
двадцать тридцать сорок пятьдесят шестьдесят
семьдесят восемьдесят девяносто }
set names(900) { {}
сто двести триста четыреста пятьсот шестьсот
семьсот восемьсот девятьсот}
set major_triads {
{1 тысяч а и {} 0}
{0 миллион {} а ов 0}
{0 миллиард {} а ов 0}
{0 триллион {} а ов 0}
{0 квадриллион {} а ов 0}
{0 квинтиллион {} а ов 0}
{0 секстиллион {} а ов 0}
}
set rur_descrip {0 рубл ь я ей 1}
set kop_descrip {1 копе йка йки ек 1}

proc spell {num descrip {usercall 0}} {
variable names
foreach {female root one two five spellZero} [lindex $descrip 0] {break}
set sexindex [lindex {0 end} $female]
if {[string length $num] <4} {
if {(![string length $num]) || (!$num)} {
if {$spellZero} {
if {!$usercall} {
return $root$five
} else {
return "[lindex $names(19) 0] $root$five"
}
} else {return {}}
}
if {$num<20} {
switch $num {
1 {set suffix $one}
2 - 3 - 4 {set suffix $two}
default {set suffix $five}
}
return "[lindex $names(19) $num $sexindex] $root$suffix"
} elseif {$num<100} {
return \
"[lindex $names(90) [expr {$num/10}]] [spell [
expr {$num % 10}] $descrip]"
} else {
return \
"[lindex $names(900) [expr {$num/100}]] [spell [
expr {$num % 100}] $descrip]"
}
} else {
return "[string trim [spell [
string range $num 0 end-3] [lrange $descrip 1 end]
]] [spell [string trimleft [string range $num end-2 end] 0] $descrip]"
}
}
proc capitalize {str} {return [string toupper [string index $str 0]][
string range $str 1 end]}
proc spell-rur {num} {
variable major_triads
variable rur_descrip
variable kop_descrip
foreach {rub kop} [split $num .] {break}
set str_rub [
capitalize [spell $rub [concat [list $rur_descrip] $major_triads] 1]]
set str_kop "$kop [lindex [spell $kop [list $kop_descrip]] end]"
return $str_rub\ $str_kop
}
}
puts [
speller::spell-rur 123000100.34
]
puts [
speller::spell-rur 0.00
]
puts [
speller::spell-rur 2718281231415926.53
]

Как я управляю пакетами своего репозитория

Возник тут разговор на эту тему, вот и решил выложить краткое описание. Итак, обновление пакета package, загрузка его обратно в репозиторий:

# download
apt-get source package
# install build dependencies
apt-get build-dep package
#... edit package
# update changelog (and version)
dch -i
# build updated package
debuild -us -uc -sa
# remove old version from local repository
reprepro removesrc lenny package
# include new version into local repository
reprepro -C main include lenny /mnt/work/build/...package...changes
# update remote repository
rdiff-backup --exclude '**/conf' --exclude '**/db' /mnt/work/reprepro/ .../debian


Новая версия установится автоматически после стандартного:

sudo aptitude update && sudo aptitude upgrade


Upd.
Не упомянул про вход в чрут ленни для сборки, но многие пакеты этого не требуют (метапакеты, пакеты с настройками или скриптами, etc.).

Метапакет с моим десктопом

В силу перехода с КДЕ на IceWM, сделал метапакет для автоматической установки необходимых мне на десктопе программ:


sudo aptitude install mbg-desktop


Соответственно, в дальнейшем, будет просто обновляться список зависимостей этого пакета. В ближайших планах выложить пакет с системой монтирования USB-накопителей и файл тулбара IceWM, настройки IceWM и так далее. Разумеется, все новые зависимости будут установлены системой apt автоматически.

Пользуюсь частью КДЕ-шных (kmail, kopete, konqueror), но их не прописывал (пока?). Надо сначала глянуть, сколько того самого КДЕ они за собой потянут.

понедельник, 18 января 2010 г.

Правильное монтирование USB-устройств

Добрался сделать свою утилиту для монтирования USB-накопителей. Как водится, долго думал, после чего стало ясно, что ни одной "правильной" системы монтирования я не видел. Все дело в том, что пользователь работает с физическими устройствами, а не дисками в их понимании ОС, и тем более не с их разделами. То есть требуется следующий функционал: примонтировать/отмонтировать/перемонтировать все и примонтировать/отмонтировать последнее подключенное устройство. Все, больше ничего не нужно. Но вот отсутствие именно этих двух возможностей и делает все системы монтирования настолько неудобными, какие они есть. Реализация в виндоус, кстати, не исключение, - сколько раз сам наблюдал, когда пользователь среди множества разделов (сетевые шары, внешний жесткий диск, флэшка) пытается "узнать" последний подключенный, чтобы отмонтировать именно флэшку.

Пакет пока не выложил, но скоро будет. Называется он mbg-usbmount и содержит одноименную программу для монтирования, являющуюся обвязкой к замечательной утилите pmount, и файл описания тулбара для IceWM.

Все накопители монтируются в /media по их метке или, при ее отсутствии, по идентификатору. При отмонтировании соответствующие папки из /media удаляются даже для тех устройств, которые были жестоко выдернуты без отмонтирования. Уж не знаю, почему большинство систем монтирования в таких случаях папки сохраняет, заставляя пользователя лазить по ним и искать, где же его данные лежат.

От идеи автомонтирования по событиям UDEV я отказался, т.к. определение пользователя, для которого следует замонтировать накопитель, становится явно неоднозначно решаемой задачей. Раз у нас линукс, то на одной машине могут работать несколько пользователей одновременно, и вменяемая система монтирования не должна монтировать флэшку пользователя А с правами пользователя Б ни в каком случае. Так что оказывается, что монтирование должно выполняться по указанию пользователя, но процесс следует упростить до сценариев "подключить все накопители, которые я тут навтыкал" или "подключить тот накопитель, который я сейчас воткнул". Все, список накопителей показывать вовсе не нужно, ибо требуемые действия легко автоматизируются. Еще иногда случаются сбои, тут просто нужно перемонтировать все найденные разделы, удалив неактуальные точки монтирования.

Утилиты КЛАДР

Статья перемещена по адресу http://sqlite.mobigroup.ru/wiki?name=sqlite3-kladr

Измерим шеллы в попугаях

Иногда возникает вопрос, какой шелл быстрее. Измеряем скорость запуска на уже не раз упоминавшемся CoreQuad 2.66GHz:


$ time echo " "|dash

real 0m0.001s
user 0m0.000s
sys 0m0.000s

$ time echo " "|bash

real 0m0.002s
user 0m0.000s
sys 0m0.004s

$ time echo " "|perl

real 0m0.002s
user 0m0.000s
sys 0m0.000s

$ time echo " "|zsh

real 0m0.003s
user 0m0.004s
sys 0m0.000s

$ time echo " "|tclsh8.4

real 0m0.005s
user 0m0.004s
sys 0m0.004s

$ time echo " "|tclsh8.5

real 0m0.007s
user 0m0.004s
sys 0m0.000s

$ time echo " "|tclsh8.6

real 0m0.007s
user 0m0.008s
sys 0m0.000s


Как видим, весьма мудро в дебиане решили выкинуть bash в пользу dash. Правда, дефолтовый /bin/sh указывает в ленни на /bin/bash, так что нелишним будет в скриптах явно писать /bin/dash. Это полезно как с точки зрения быстродействия, так и потому, что в дальнейшем не придется разбираться, нет ли где в коде "башизмов". zsh приятно удивил, хотя именно для скриптов его редко используют, а для интерактивной работы разница скорости запуска в единицы миллисекунд, как правило, несущественна.

Касаемо тикля - имеет смысл уже сейчас переходить на 8.6 в "мелких" скриптах, т.к. скорость запуска идентична таковой для версии 8.5, а к релизу, если повезет, может еще улучшиться. Версия 8.4 для новых приложений интереса уже не представляет.

суббота, 16 января 2010 г.

Google AJAX Libraries API

Согласно Developer's Guide:

The AJAX Libraries API is a content distribution network and loading architecture for the most popular, open source JavaScript libraries. By using the google.load() method, your application has high speed, globally available access to a growing list of the most popular, open source JavaScript libraries.


Что удобно, так это наличие прямых ссылок на последние релизы библиотек, по которым можно скачать с помощью wget, вместо тыканья по разным менюшкам на офсайтах.

1С-Предприятие 7.7 и SQLite

Вот интересная ссылка:
Работа в 1С-Предприятии 7.7 с базами данных SQLite

Автора этого проекта можно найти в форуме SQLite на sql.ru

Сам я никогда с 1C не работал, успешно избегая сей участи, но система широко распространенная, так что возможность интеграции с ней вполне востребована. Если этот компонент поможет обмениваться данными, не влезая в саму 1C, то профит.

четверг, 14 января 2010 г.

Автомонтирование устройств

При монтировании CD/DVD дисков под utf8 локалью кириллица в именах файлов нечитабельна. Есть несколько путей решения вопроса, в том числе отключение rock-расширений ФС на диске:

$ cat /etc/fstab|grep cdrom
/dev/sr0 /media/cdrom0 udf,iso9660 ro,user,noauto,norock 0 0

или использование утилиты fuseiso, предоставляющей возможность явно указать нужную перекодировку:

fuseiso /dev/sr0 /media/cdrom0 -omodules=iconv,from_code=cp1251,to_code=utf8
fusermount -u /media/cdrom0

fuseiso, кстати, понимает не только iso-формат, но и некоторые другие, широко распространенные под виндоус. Из описания: "Также он позволяет монтировать .BIN, .MDF, .ING и .NRG если они содержат только одну дорожку."

Для автомонтирования usb-устройств пригодится пакет usbmount, представляющий собой небольшой файлик с набором udev-правил и несколько шелл-скриптов для монтирования устройств, причем проверяется существование параметров монтирования для подключенного устройства в fstab, прежде чем ииспользовать значения по умолчанию.

Осталось добавить к этому набору GUI для просмотра списка устройств и их отмонтирования пользователем. Вероятно, этот функционал стоит встроить в какой-нибудь файловый менеджер, который еще предстоит выбрать (я обычно использую mc, но для пользователей это неудобно).

Upd.

Tux Commander неплох. cdrom он без лишних заморочек монтирует и отмонтирует согласно записи в fstab, автоматически примонтированные усб-устройства отмонтирует. Осталось разобраться, как его научить без ругани сетевые шары перемонтировать (отпадают после спящего режима) - можно и без этого обойтись, запихнув в скрипт пробуждения, но почему бы и не в туксе это делать, меньше будет самописных скриптов в системе.

Вот что удивило, так это отсутствие файловых ассоциаций в туксе - все надо прописывать вручную. Хотя, с другой стороны, я давно уже хотел нафиг пришибить все КДЕ-шные ассоциации и сделать свои. Скажем, все видео открывать mplayer (или mplayer.sh, если нужны собственные настройки) и т.п.

Прощай, KDE

В целях испытания на себе KDE 4, прежде чем ставить его близким (сейчас используют KDE 3.5) с полгода я жил с этим, хм, изделием. Итак, пришла пора подвести итоги.

Начнем с того, что ставить KDE 4 можно разве что заклятому врагу, и не приведи вам судьба в это вляпаться. Даже при том, что я использую всего несколько КДЕ-шных программ, оно меня успело достать до чертиков. Я уж не говорю про такие "мелочи", что раскладку клавиатуры в КДЕ 4.1 однажды починили, громко по этому поводу кричали (а как же - много лет не могли исправить этот баг), после чего почти сразу же сломали и больше не чинят. В конкуероре периодически "отваливается" работа с файловой системой - дерево каталогов показывает, а вот список файлов не работает, при этом отображается сообщение "Malformed URL". Про стабильность работы могу лишь сказать, что текущая версия по стабильности работы отстает от КДЕ 3.5, а по утечкам памяти, наоборот, опережает. В настоящий момент монитор батареи ноутбука пытается меня уверить, что батареи у меня нет, равно как и внешнего питания (пару дней назад скурвился, до того как-то работал). Пресловутая plasma периодически показывает сообщение о свое бесславной кончине, впрочем, само перезапускается и продолжает как бы работать.

Про адекватность разработчиков можно слагать легенды. Персональная информация ныне хранится в mysql-базе, причем недоделанный проект Akonadi "развивается" силами пары разработчиков, - трудно поверить, что апстрим решил все бросить и пересесть на Akonadi, но именно так и произошло. Эти самые упомянутые разработчики в ответ на вопрос, на кой ляд на десктопе клиент-сервер mysql отвечают, что больше они ни с чем работать не умеют и тчто так сойдет. На предложение помочь сделать на SQLite ответить не удосужились, на этом моя переписка с ними прекратилась. Закладки в konqueror тоже планируют перенести в Akonadi, впрочем, эти самые закладки при попытке их редактировать стабильно накрываются медным тазом, так что не велика потеря, можно их и вовсе никуда не писать, мало что изменится.

Разобраться в структуре создаваемых в домашнем каталоге пользователя файлов я уже перестал и пробовать, поскольку в этой свалке о структуре и речи нет. Свалку эту я периодически ручками от всяких временных файлов чищу, и тем не менее сейчас она около полутора гигов места на диске занимает. Диски сейчас большие, это верно, но сделать дамп настроек приложений, как минимум, затруднительно, поскольку эти настройки надор еще раскопать, а если дампать все, то часто меняющиеся временные файлы генерируют немалый размер инкрементального дампа.

НЕмного позитива - часть приложений существенно переработаны и стали намного удобнее, например, kmail и kget. Но эти приложения можно использовать и без КДЕ как такового, так что это стоит отнести к плюсам обновленной библиотеки Qt.

В качестве эпитафии. KDE4 - мертвая ветвь эволюции, причем весь проект KDE вряд ли эту версию вообще переживет. Отсутствие архитектуры - я уж и не говорю о хорошо продуманной, так ведь вообще никакой нет, совершенно сырые приложения от тяп-ляп кодеров в качестве ключевых элементов инфраструктуры, отсуствие какого-либо контроля качества и, как следствие, появление новых багов и безуспешные попытки исправить старые - вот краткий обзор состояния проекта. С КДЕ 3.5 я жил лет пять (до этого использовал IceWM), теперь ищу альтернативу (как самый простой вариант, снова вернусь на IceWM, но уж явно не стану менять шило на мыло, то есть КДЕ на гном).

среда, 13 января 2010 г.

Система полнотекстового поиска sqlite3-poisk

Статья не завершена.

Пакет sqlite3-poisk уже доступен в моем debian-репозитории.

Индексатор

Пример использования:
poisk-scanner share.db /mnt/project/www/share /mnt/project/www

После выполнения указанной команды будет создана БД share.db в текущей директории с результатами индексирования директории /mnt/project/www/share. Пути к проиндексированным файлам отсчитываются от директории /mnt/project/www - это удобно для поиска по файлам веб-проектов, чтобы сразу получать ссылки относительно корневой директории сайта.

Утилита poisk-scanner-mbg предназначена для индексации веб-проектов, структура директорий которых совпадает с используемой автором.

TODO
В настоящее время поисковые запросы интегрированы в мой веб-портал, но я планирую их также реализовать в отдельной утилите poisk-http, которая будет принимать HTTP-запросы через stdin. Для веб-доступа к утилите рекомендую использовать tcpserver.

Запрос от другой системы на поиск документов, располагающихся в дереве директорий ниже пути /path, должен иметь следующий вид:
X-POISK-PATH path
X-POISK-QUERY query
X-POISK-LIMIT max_count
X-POISK-OFFSET offset_count

Запрос для получения документа по его пути uri:
X-POISK-URI uri
X-POISK-QUERY query

При необходимости обеспечить веб-доступ пользователей непосредственно к утилите poisk более удобным является указание нужных параметров в запросе HTTP GET:
GET /path?q=query&limit=max_count&offset=offset-count HTTP/1.1
GET /uri?q=query HTTP/1.1

Начальная реализация будет базироваться на расширении ncgi.

Результаты поиска возвращаются в формате xHTML и имеют вид:
...


Планируемая производительность составляет 100 поисковых запросов в секунду на 1 ядре процессора уровня CoreQuad. Для обработки тысяч запросов в секунду необходимо использовать аналогичную реализацию на C, за основу можно взять веб-сервер fnord, работающий через tcpserver.

Upd. Проект опакечен и задокументирован, см. Poisk - Система полнотекстового поиска.

Tcl на arm-android и другое

Alexander Danilov прислал интересные ссылочки:
/tclkit/android-arm/

Judy Arrays

Exploring Expect (477 страниц)

Насчет этой ссылки мнения разделились, но все же:
Tcl3D offers the 3D functionality of OpenGL and other 3D libraries at the Tcl scripting level.

А вот еще попалось на глаза:
The L Programming Language

True Unix Way

Проходят дни и годы, и начинаю все больше замечать, что с помощью маленьких, с умом реализованных утилит эффективно выполняю большую часть работы, а на различных монстров тратится львиная доля времени, причем, по сути, эти монстры решают всего-то пару задач. Как пример - KDE 4.x предоставялет мне несколько полезных программ, таких, как
kmail,
kget,
kopete,
kjots,
запуск приложений по ALT+F2,
ksnapshot (иногда),
konqueror,
kwrite,
k3b (иногда).

"Прочее" реализуется еще несколькими программами:
mozilla firefox
terminal
skype (очень иногда)
open office (иногда)
pgadmin (иногда)
virtualbox (иногда)
transmission,
dvdrip (очень иногда),
avidemux (очень иногда),
gimp (очень иногда),
mplayer,
музыкальный плеер (имхо все они глючные, так что выбираю каждый раз по ситуации).

И... все! Притом времени на возню с КДЕ, начиная с версии 3.5, потрачено неоправданно много. Множество возникающих каждый день задач выполняются посредством консольных утилит, причем выполняются эффективно. Как пример,
настроить мониторы (встроенный в ноут отключить и внешний сконфигурировать):

xrandr --output VGA --mode 1920x1080 --output LVDS --off

подключиться к сервису на удаленном сервере:

ssh -L 5000:localhost:5000 хост

Загрузить новуюю версию пакета в репозиторий:

reprepro removesrc lenny sqlite3
reprepro -C main include lenny /mnt/work/chroot/lenny/srv/sqlite3/sqlite3_3.6.18-mobigroup.1.changes
rdiff-backup --exclude '**/conf' --exclude '**/db' /mnt/work/reprepro/ хост::директория

И это не говоря о полностью автоматизированных операциях, в число которых входит монтирование удаленных файловых систем и проч.

Понятно, что периодически возникает мысль о том, что все неладно в сфере десктопов. Вот пара статей на эту тему:

True Unix GUI

True Unix GUI 2.0

О вреде дружественных интерфейсов.

P.S. Еще и подсмотрел в указанных статьях полезную утилиту:

zenity --file-selection

Можно так же и другие диалоги вызывать. Для скриптования самое оно, пригодится.

суббота, 9 января 2010 г.

mbg-opendocument-[server|client]

Внимание: при изменении версии openoffice необходимо переустановить пакет odtfactory-server.

Внимание: пакеты переименованы и создан отдельный пакет с файлом конфигурации:
odtfactory-client odtfactory-config odtfactory-server


Ограничения для отдельного процесса конвертации (для одностраничного шаблона нужно около 200 Mb памяти, которые занимают openoffice и ява, а с указанными настройками можно обрабатывать достаточно большие документы):
ulimit -m 300000
ulimit -v 300000
ulimit -f 2000

Размер выходного файла не может превышать 8 Mb, для подавляющего большинства применений этого лимита хватает с избытком.

Обработчик каждого формата принимает не более 3-х одновременных подключений (настраивается в /etc/service/odt2*/run скриптах).

Архитектура

Клиент-серверное взаимодействие выполняется путем двусторонней передачи бинарных файлов, при этом никакая служебная информация не передается. Для контроля целостности файлов на стороне клиента и сервера следует воспользоваться анализаторами для файлов соответствующего формата (например, см. статью Работа с файлами формата OpenDocument).

odtfactory-server - принимает через сокет файл формата OpenDocument на обработку и возвращает в требуемом формате. Для каждого типа результирующих документов используется отдельный порт.

Настройки по умолчанию:
$ cat /etc/odtfactory/config.sh
# settings for openoffice.org and java
#export LANG=ru_RU.UTF8

# odt2doc service location
ODT2DOCHOST=127.0.0.1
ODT2DOCPORT=3001

# odt2pdf service location
ODT2PDFHOST=127.0.0.1
ODT2PDFPORT=3002


Здесь переменная LANG может быть необходима для openoffice (если при конвертации выдает ошибки определения локали, нужно раскомментировать эту строку в конфиге).

На сервере odtfactory-server обработка производится с использованием 2-х временных файлов (оригинального и результирующего).

odtfactory-client - отправляет файл серверу и принимает результат.

На клиенте можно открыть пайп и читать данные, или сохранить во временный файл. Исходные данные могут быть как получены из пайпа, так и из файла.

odt2doc@, odt2pdf@ - утилиты для преобразования OpenDocument файла в соответствующий формат, выполняют пересылку файла серверу и принимают результат.

Тесты на ноутбуке Core2Duo, частота 800 MHz, 1Gb RAM:
$ apt-cache policy openoffice.org
openoffice.org:
Установлен: 1:2.4.1+dfsg-1+lenny3

# time odt2doc@ /tmp/100.odt > /tmp/x.doc
$ time cat /tmp/100.odt | odt2doc@ > /tmp/x.doc

real 0m3.330s
user 0m0.020s
sys 0m0.004s

# time cat /tmp/100.odt | odt2pdf@ > /tmp/x.pdf
$ time odt2pdf@ /tmp/100.odt > /tmp/x.pdf

real 0m3.482s
user 0m0.004s
sys 0m0.016s


С более новым openoffice:
$ apt-cache policy openoffice.org
openoffice.org:
Установлен: 1:3.1.1-8

$ time odt2doc@ /tmp/100.odt > /tmp/x.doc

real 0m2.382s
user 0m0.008s
sys 0m0.012s

$ time odt2pdf@ /tmp/100.odt > /tmp/x.pdf

real 0m2.560s
user 0m0.020s
sys 0m0.004s


И с последней версией openoffice:
$ apt-cache policy openoffice.org
openoffice.org:
Установлен: 1:3.2.0~rc1-1

$ time odt2doc@ /tmp/100.odt > /tmp/x.doc

real 0m2.362s
user 0m0.012s
sys 0m0.004s

$ time odt2pdf@ /tmp/100.odt > /tmp/x.pdf

real 0m2.275s
user 0m0.012s
sys 0m0.008s


По возможности стоит использовать свежую версию openoffice, т.к. разница в быстродействии очень значительна.

Upd.
Если преобразование не работает, или первой строкой в созданном файле указано javaldx: Could not find a Java Runtime Environment!, следует проверить, что в профиле openoffice правильно определена java:
sudo less ~odtfactory/.openoffice.org2/user/config/javasettings_Linux_x86.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--This is a generated file. Do not alter this file!-->
<java xmlns="http://openoffice.org/2004/java/framework/1.0" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<enabled xsi:nil="true"/>
<userClassPath xsi:nil="true"/>
<vmParameters xsi:nil="true"/>
<jreLocations xsi:nil="true"/>
<javaInfo xsi:nil="false" vendorUpdate="2004-01-30" autoSelect="true">
<vendor>Sun Microsystems Inc.</vendor>
<location>file:///usr/lib/jvm/java-6-sun-1.6.0.12/jre</location>
<version>1.6.0_12</version>
<features>0</features>
<requirements>1</requirements>
<vendorData>660069006C0065003A002F002F002F007500730072002F006C00690062002
...
32002F006A00720065002F006C00690062002F0069003300380036000A00</vendorData>
</javaInfo>
</java>


Случается такое, что после установки openoffice ява никоими манипуляциями не находится и тогда в указанном файле профиля нужных строк нет. Рекомендую выполнить вот такую команду:
# /usr/bin/Xvfb :10 -from localhost -nolisten tcp
$ sudo -H -u odtfactory soffice -invisible -headless -norestore \
"macro:///odtfactory.Template.ConvertToDOC(/tmp/100.odt)" -nologo


В общем, черт бы побрал эту яву и программистов на побочных эффектах.

Итак, вот как выглядит "лечение":
$ odt2doc@ /tmp/100.odt /tmp/100.doc
$ head -n1 /tmp/100.doc 
javaldx: Could not find a Java Runtime Environment! 

$ rm 100.doc 
$ sudo -H -u odtfactory soffice -invisible -headless -norestore \
"macro:///odtfactory.Template.ConvertToDOC(/tmp/100.odt)" -nologo
$ head -n1 /tmp/100.doc 
�� ࡱ �; ��   �   
$ extract /tmp/100.doc 
mimetype - application/vnd.ms-office


Как видим, ява успешно нашлась, и все заработало, но не спрашивайте меня, почему.

Upd.

На сервере с процессором Intel(R) Core(TM)2 Quad CPU Q6700 @ 2.66GHz имеем следующие показатели:
$ apt-cache policy openoffice.org
openoffice.org:
Installed: 1:2.4.1+dfsg-1+lenny3

$ time odt2doc@ /tmp/100.odt /tmp/100.doc

real 0m0.877s
user 0m0.000s
sys 0m0.004s


Если сравнить с результатом на ноутбуке (см. выше), то мы получили практически линейную зависимость от частоты. Таким образом, несложно подсчитать, что openoffice 3.2 на CoreQuad обработает тот же файл примерно за полсекунды.

Upd.

openoffice 2.3 можно запустить и без виртуальных иксов, установив пакет openoffice.org-headless.

А если запускать напрямую /usr/lib/openoffice/program/soffice.bin, то выполнение команды окажется примерно на треть быстрее (для простых документов, когда запуск openoffice занимает львиную долю времени). Тест на том же сервере:
$ time odt2doc@ /tmp/100.odt /tmp/100.doc

real 0m0.668s
user 0m0.004s
sys 0m0.000s



Примечание: Категорически не рекомендую пытаться использовать управляющий сокет openoffice! Во-первых, при работе с ним наблюдается утечка памяти. Во-вторых, протокол часто меняется, что приводит к невозможности обновить версию openoffice (например, когда я отладил работу tcluno с ooo 2.3, с ooo 2.4 эта библиотека отказалась работать из-за изменения версии протокола).

По результатам своих тестов могу утверждать - для неадминистрируемого сервера единственный вариант - запускать отдельный процесс openoffice для каждой конвертации. На сервере с процессором CoreQuad и 8Gb RAM ежедневная конвертация нескольких тысяч документов не создает никакой ощутимой нагрузки.

пятница, 8 января 2010 г.

Виртуальный X-server

Случается, что необходимо запустить программу, привязанную к использованию X-server. В подобных случаях может помочь пакет xvfb. Сценарий runit для запуска приведен ниже:

# cat /etc/sv/X/run
#!/bin/sh

export LANG="ru_RU.UTF8"
export HOME="/home/ooo"

exec 2>&1
exec chpst -u nobody:nogroup /usr/bin/Xvfb -screen 0 800x600x24 :10 \
-from localhost -nolisten tcp


Есть один неприятный момент - иногда по необъяснимым причинам x-server перестает запускаться с указанным номером дисплея и удаление /tmp/.X11-unix не помогает.

Во избежание указанной проблемы предусмотрен "финт ушами", причем опции " -from localhost -nolisten tcp" уже не требуются, tcp-порт по умолчанию не открывается. В примере запускаю команды одновременно в двух разных консолях:

$ xvfb-run --auto-servernum --server-args='-screen 0 640x480x8' sh -c "echo \$DISPLAY"
:99

$ xvfb-run --auto-servernum --server-args='-screen 0 640x480x8' sh -c "echo \$DISPLAY"
:100


Как видим, теперь несколько экземпляров xvfb могут работать независимо, а автоматически назначенный номер дисплея мы получаем из переменной окружения DISPLAY.

Запуск иксов, даже виртуальных, занимает время порядка нескольких секунд, и потому неплохо будет сделать что-то вроде следующего:

$ xvfb-run --auto-servernum --server-args='-screen 0 640x480x8' \
/usr/bin/tcpserver -R -H 127.0.0.1 3000 ./env@

$ http@ localhost /test 3000|grep DISPLAY
DISPLAY: :99


И напоследок тест запуска openoffice.org на ноутбуке с процессором Core2Duo и 1 Gb RAM:

$ time xvfb-run --auto-servernum --server-args='-screen 0 640x480x8' \
sh -c "soffice -invisible -headless -norestore \
\"macro:///MbgServer.Template.ConvertToPDF(/tmp/100.odt)\" \
-display \$DISPLAY -nologo"

real 0m6.021s
user 0m2.564s
sys 0m0.288s


Кстати, в теории, используя cups-pdf принтер можно выводить любые документы "на печать" примерно вот так:

soffice -invisible -headless -norestore -pt local /tmp/100.odt -nologo

На практике не пробовал, поскольку лучше уж макрос на том самом встроенном бейсике написать, нежели CUPS на сервере ставить.

При необходимости см. документацию на опенофис здесь: Documents & files: Specifications

Upd.

При более внимательном чтении манов обнаружилось, что xvfb-run может запускаться практически мгновенно, используя ключик "-w 0", а по умолчанию стоит задержка 3 секунды (т.е. используется значение -w 3), что и было видно в тестах выше. Проверил "xvfb-run -w 0" - никаких проблем, все работает стабильно, и не очень понятно, зачем придумана эта задержка.

Secure Remote Log Transmission System

А вот нашлась и система логирования на удаленный хост, совместимая с daemontools.

Анонс: Secure Remote Log Transmission System
Описание: Srlog2 Design Documentation

deb-пакета нет, но есть описание, как эту утилиту опакетить и применять : srlog2: secure remote logging

Using AOLserver as a proxy server

В некоторых случаях удобно переадресовать или сгенерировать запрос к тому или иному бэкенду, нежели обрабатывать его средствами AOL Server. Отправка запроса реализуется достаточно легко с помощью встроенной функции ns_httpopen:
Using AOLserver as a proxy server

set conn [ns_httpopen GET $some_arbitrary_URL {} 30]
set read_desc [lindex $conn 0]
set write_desc [lindex $conn 1]
set headers [lindex $conn 2]

set mime_type [ns_set get $headers content-type]

set head "HTTP/1.0 200 OK
MIME-Version: 1.0
Content-Type: $mime_type\r\n\r\n"
# Plus any more headers you need.

ns_write $head
ns_startcontent -type $mime_type
ns_writefp $read_desc


Для перенаправления запроса потребуется лишь скопировать все или некоторые хидеры исходного запроса (в примере выше хидеры не передаются). Таким образом, возможно, к примеру, выполнение авторизации в аоле и обработка запроса бэкенд-сервером.

См. также реализацию http-клиента для аола на основе cURL: Topic: AOLserver -- HTTP client.

Бэкендом может быть любая консольная утилита, доступ к которой обеспечивается с помощью tcpserver из комплекта ucspi-tcp. В том числе интересен fnord - веб-сервер, представляющий собой очень компактную программу (16 kB), вызываемую через tcpserver.


$ tcpserver -RHl localhost 0 33333 /usr/sbin/fnord-idx .

$ ls -lh test.js
-rw-r--r-- 1 veter veter 2.1K 2009-05-14 16:20 test.js

$ openload localhost:33333/test.js
URL: http://localhost:33333/test.js
Clients: 5
MaTps 7110.00, Tps 7110.00, Resp Time 0.001, Err 0%, Count 7110
MaTps 7099.80, Tps 7008.00, Resp Time 0.001, Err 0%, Count 14118
MaTps 7083.82, Tps 6940.00, Resp Time 0.001, Err 0%, Count 21058
MaTps 7072.34, Tps 6969.00, Resp Time 0.001, Err 0%, Count 28027
MaTps 7060.90, Tps 6958.00, Resp Time 0.001, Err 0%, Count 34985
MaTps 7068.81, Tps 7140.00, Resp Time 0.001, Err 0%, Count 42125
MaTps 7079.33, Tps 7174.00, Resp Time 0.001, Err 0%, Count 49299
MaTps 7071.70, Tps 7003.00, Resp Time 0.001, Err 0%, Count 56302
MaTps 7080.63, Tps 7161.00, Resp Time 0.001, Err 0%, Count 63463
MaTps 7075.27, Tps 7027.00, Resp Time 0.001, Err 0%, Count 70490

Total TPS: 6869.03
Avg. Response time: 0.001 sec.
Max Response time: 0.029 sec
Total Requests: 70490
Total Errors: 0

$ time echo -e "GET /test.js HTTP/1.1\r\n"|/usr/sbin/fnord-idx . >/dev/null
0.0.0.0 - - [08/Jan/2010:00:51:24 -0300] "GET /test.js/test.js HTTP/1.1" 200 2054

real 0m0.001s
user 0m0.000s
sys 0m0.004s

среда, 6 января 2010 г.

Работа с файлами формата OpenDocument

Начнем с хорошего - контейнером является все-таки стандартный zip-архив, а не что-то доморощенное. Теперь о плохом - это zip-архив и имеет все его недостатки. Поговорим о том, как же нам сделать редактирование контента для целей, например, автоматизированного вывода на печать документов - распаковываем файл content.xml, выполняем в нем нужные замены с помощью sed или другой утилиты, записываем измененный файл обратно в архив.

Две основные проблемы при работе с zip-архивами: утилита zip написана страшными голово...ногими существами с неизвестной планеты и сообщения об ошибках в архиве пишет в stdout (при запуске zip -T ...). Повбивав бы... Кроме того, не умеет заменять файл в архиве, принимая сам файл в stdin, а его имя - аргументом командной строки.

Решение первой проблемы - утилита unzip:

$ time unzip -qqt 100.odt

real 0m0.006s
user 0m0.004s
sys 0m0.000s

$ time unzip -qqt 100 >/dev/null
warning [100]: 3 extra bytes at beginning or within zipfile
(attempting to process anyway)
file #1: bad zipfile offset (local header sig): 3
(attempting to re-compensate)
Pictures/10000000000000AA000000528155B835.png: mismatching "local" filename (),
continuing with "central" filename version
Pictures/10000000000000AA000000528155B835.png: ucsize 174417162 <> csize 5130 for STORED entry
continuing with "compressed" size value
Pictures/10000000000000AA000000528155B835.png bad CRC d80cc663 (should be c116ead1)
file #11: bad zipfile offset (local header sig): 5746
(attempting to re-compensate)

real 0m0.006s
user 0m0.000s
sys 0m0.004s

Ошибки отправляются в stderr, что и требовалось.

Решение второй проблемы - утилита zipput:

echo "hello" | zipput test.odt content.xml

Эта замечательная утилита была придумана вчера, в процессе обсуждения subj с Anton Kovalenko, который тут же написал реализацию, а я ее сразу опакетил, так что уже можно брать в моем репозитории. Подробности см. на страничке автора zipput. Отмечу лишь, что libzip и zip в debian lenny некорректно работают с OpenDocument файлами, так что я сразу сделал и бэкпорт соответствующих пакетов из testing, с которыми все работает как полагается (в changelog новой версии указано "Add patch to support archives with 64k entries").

Размышления о безопасности qmail от DJB

Ненароком попалась на глаза замечательная ссылочка:
Some thoughts on security after ten years of qmail 1.0

Клиент-серверное взаимодействие

Предисловие, написанное после завершения заметки: все тесты выполнялись на ноутбуке core2duo, со сниженной до 800Мгц частотой процессора (для мирного использования дома хочется тишины). На десктопе с coreQuad процессором время уменьшится в несколько раз (имхо примерно втрое).

Воспользуемся утилитами DJB. Проверим скорость:


$ time ./header@ localhost / 8081
HTTP/1.0 200 OK
Date: Tue, 5 Jan 2010 22:12:53 GMT
Connection: close
Cache-control: no-cache, no-store
Content-Type: text/html; charset=utf-8
Content-Length: 1502

real 0m0.017s
user 0m0.008s
sys 0m0.012s

$ time ./responce@ localhost / 8081 > test.html

real 0m0.018s
user 0m0.008s
sys 0m0.008s

$ time cat test.html |./header
HTTP/1.0 200 OK
Date: Tue, 5 Jan 2010 22:22:47 GMT
Connection: close
Cache-control: no-cache, no-store
Content-Type: text/html; charset=utf-8
Content-Length: 1502

real 0m0.009s
user 0m0.004s
sys 0m0.004s


Как видим, отправка запроса и получение ответа с помощью tcpclient занимает около 9 мс (включая время на преобразования переводов строк).

Утилиты header@ и responce@ представляют собой немного модифицированный код http@:


$ cat header@
#!/bin/sh
echo "GET /${2-} HTTP/1.0
Host: ${1-0}:${3-80}
" | /usr/bin/tcpclient -RHl0 -- "${1-0}" "${3-80}" sh -c '
/usr/bin/addcr >&7
exec /usr/bin/delcr <&6
' | awk '/^$/ { body=1; next } { if (!body) {print} else {exit}}'

$ cat responce@
#!/bin/sh
echo "GET /${2-} HTTP/1.0
Host: ${1-0}:${3-80}
" | /usr/bin/tcpclient -RHl0 -- "${1-0}" "${3-80}" sh -c '
/usr/bin/addcr >&7
exec /usr/bin/delcr <&6
'


Передача бинарных данных

Сервер можно организовать разными способами, рассмотрим два из них.
Первый вариант с помощью утилиты socat:

$ socat tcp-l:3000,reuseaddr,fork system:'md5sum',nofork

Второй вариант с помощью tcpserver (воспользуемся трассировкой ввода-вывода через recordio):

$ tcpserver -R -H 127.0.0.1 3000 recordio md5sum
12871 < Hello!
12871 < [EOF]
12871 > e134ced312b3511d88943d57ccd70c83 -
12871 > [EOF]


Клиент в обоих случаях следующий:

$ ./client.tcl
e134ced312b3511d88943d57ccd70c83 -

$ cat ./client.tcl
#!/usr/bin/tclsh8.6
set s [socket localhost 3000]
fconfigure $s -translation binary -buffering none
puts $s "Hello!"
close $s w
puts [read $s]
close $s


Замечу, что использован интерпретатор Tcl 8.6, т.к. для получения результата от потоковой утилиты md5sum нам необходима поддержка полузакрытых соединений (half-closed).

Теперь посмотрим, как организована передача данных в приложение, запускаемое на стороне сервера (для получения более подробного вывода используйте filan без ключа -s, и с ключом -s для краткого вывода, здесь же я привожу профильтрованный awk вывод, чтобы не загромождать текст):


$ socat tcp-l:3000,reuseaddr,fork system:'filan',nofork
$ ./responce@ localhost / 3000|awk '{print $1 "\t" $2}'
FD type
0: socket
1: socket
2: chrdev

$ tcpserver -R -H 127.0.0.1 3000 filan
$ r$ ./responce@ localhost / 3000|awk '{print $1 "\t" $2}'
FD type
0: socket
1: socket
2: chrdev

$ tcpserver -R -H 127.0.0.1 3000 recordio filan
$ telnet localhost 3000
$ ./responce@ localhost / 3000|awk '{print $1 "\t" $2}'
FD type
0: pipe
1: pipe
2: chrdev


Чтобы узнать полную информацию о параметрах сокетов, просто отключите фильтрацию вывода через awk.

Теперь, когда технология и реализация понятны, реализуем решение практической задачи.

Способ первый, через tcpserver:

$ tcpserver -R -H 127.0.0.1 3000 md5sum

$ time ./send.tcl README
34f58706e32612c4f70b480f12a051db -

real 0m0.017s
user 0m0.008s
sys 0m0.004s

$ md5sum README
34f58706e32612c4f70b480f12a051db README

Способ второй, через socat:

$ socat tcp-l:3000,reuseaddr,fork system:'md5sum',nofork

$ time ./send.tcl README
34f58706e32612c4f70b480f12a051db -

real 0m0.021s
user 0m0.008s
sys 0m0.008s

$ md5sum README
34f58706e32612c4f70b480f12a051db README


Итак, мы отправили на обработку бинарный файл, получили результат обработки и проверили его. Вариант с tcpserver работает быстрее (приведенные выше времена выполнения выбраны как наименьшие из 5-ти последовательных запусков для каждого способа). В случае использования контроля доступа выигрыш в скорости при использовании tcpserver окажется еще более заметен, т.к. socat полагается на стандартный способ, а утилиты DJB используют собственный оптимизированный метод.

Реализация утилиты send.tcl приведена ниже:

$ cat ./send.tcl
#!/usr/bin/tclsh8.6

if {$argc < 1} {
puts stderr "No_args"
exit 1
}

set s [socket localhost 3000]
fconfigure $s -translation binary -buffering none
puts $s [exec cat {*}$argv]
close $s w
puts [read $s]
close $s

понедельник, 4 января 2010 г.

Обработка email-сообщений

Полагаем, что qmail у нас уже установлен и настроен. Текущая задача заключается в том, чтобы автоматизировать обработку сообщений. Будем использовать пакеты mpack, mailtextbody (или mimedecode - если хотим получить сообщение целиком, со всеми хидерами, но декодированное, вот только жаль, что utf-8 не понимает), mess822 (последний доступен в моем репозитории). Что интересно, из найденных мною обработчиков email ни один даже близко не стоял рядом с утилитами DJB из пакета mess822. Собственно, в очередной раз убедился, что современные девелоперы способны испаскудить любую хорошую идею, понаписав мегабайты [глючного] кода - ни одного нормального email-клиента, состоящего, согласно идеологии unix, из набора простых утилит, в репозитории дебиана не нашлось. Ну и черт с ними, лучше разобраться и пропатчить mess822, нежели пользовать более "современные" поделия.

Отправка письма со вложением

$ mpack -s test ./sqlite3-rdiff veter@veter-laptop


Отправка письма без вложения

$ echo "Ветер, привет"|new-inject -fpechnikov@sandy.ru veter@veter-laptop


Просмотр текста сообщения без вложения

$ cat ~veter/Maildir/new/1262559828.26006.veter-laptop | mailtextbody
Ветер, привет


Просмотр текста сообщения со вложениями

$ cat /home/veter/Maildir/new/1262551937.20192.veter-laptop| mailtextbody



Отлично - если текста нет, то ничего и не показывается, вложения игнорируются.

Сохранение вложений в отдельные файлы

$ munpack ~veter/Maildir/new/1262559828.26006.veter-laptop


Просмотр письма целиком

$ cat /home/veter/Maildir/new/1262559828.26006.veter-laptop | 822print |less
Subject:
From: veter@veter-laptop.veter-laptop
Date: 3 Jan 2010 23:03:48 -0000 2010-01-04 02:03:48
Return-Path: return pechnikov@sandy.ru
Delivered-To: veter@veter-laptop.veter-laptop
Received: (qmail 26004 invoked by uid 1000); 4 Jan 2010 02:03:48 +0300
Message-ID: 20100103230348.26003.qmail@veter-laptop.veter-laptop
------------------------------------------------------------------------------
Cc: recipient list not shown
------------------------------------------------------------------------------

Ветер, привет


Просмотр письма со вложениями

$ cat /home/veter/Maildir/new/1262551937.20192.veter-laptop| 822print |less
Subject: test
From: veter@veter-laptop.veter-laptop
Date: 3 Jan 2010 23:52:17 +0300 2010-01-03 23:52:17
Return-Path: return veter@veter-laptop.veter-laptop
Delivered-To: veter@veter-laptop.veter-laptop
Received: (qmail 20190 invoked by uid 1000); 3 Jan 2010 23:52:17 +0300
Message-ID: 20188.1262551938@veter-laptop
------------------------------------------------------------------------------
To: veter@veter-laptop.veter-laptop
------------------------------------------------------------------------------
Mime-Version: 1.0
Content-Type: multipart/mixed; boundary="-"

This is a MIME encoded message. Decode it with "munpack"
or any other MIME reading software. Mpack/munpack is available
via anonymous FTP in ftp.andrew.cmu.edu:pub/mpack/
---
Content-Type: application/octet-stream; name="mess822-0.58.tar.gz"
Content-Transfer-Encoding: base64
Content-Disposition: inline; filename="mess822-0.58.tar.gz"
Content-MD5: jOTCnJlKcNyqMBQGASE9vg==

H4sICAuj8DUCA21lc3M4MjItMC41OC50YXIA7P1bjxtJ1iiKzYMNo3P7wBvHl4MD4+BEs2dU
pIqkeKmLVKVSqySVums+3XZV6ZuerdLWZJHJYrbITDYzKVV1j7b9dJ4ObBiGAcOwj18M/wA/
+Tf4wYBf/W4YMOxHvxpea8U9M5KXUomSuqmZLmZmRKxYcVuxYsW6DIMkud1q1Rr1zdu3/vRp
...


В этом случае содержимое вложений тоже показывается. Для работы вряд ли такой вариант может быть полезен, разве что для тестирования.

Просмотр хидеров
Примечание: по умолчанию выводится значение заголовка Subject, регистр игнорируется.

$ cat /home/veter/Maildir/new/1262551990.20212.veter-laptop | 822field
Привет, Ветер!
$ cat /home/veter/Maildir/new/1262551990.20212.veter-laptop | 822field to
veter@veter-laptop.veter-laptop


Пробел после наименования заголовка отображается как первый символ в строке. Полагаю, из тех соображений, что "кривые" клиенты могут сформировать заголовки без пробелов.

Upd.

Вот так можно отправить сообщение с кириллицей на адрес me@host.my от имени me@sender.host.name:


$ echo "set from=me@sender.host.name" >> ~/.muttrc
$ echo "привет"|mutt -s "Test hello" me@host.my


Upd.
Для модификации сообщений (добавление произвольных хидеров, удаление или замена вложений) может оказаться полезным пакет altermime.

Upd.
В случае, когда мы получаем заголовки вида

$ cat /tmp/message.mbox | 822field
=?utf-8?B?0KLQtdGB0YLQvtCy0L7QtSDQvdC+0Lw=?= =?utf-8?B?0LXRgCDRgNCw0Lc=?=

можно их перекодировать чем-то вроде нижеприведенного скрипта:

package require mime
set header [exec cat /tmp/message.mbox | 822field]
foreach word [split $header] {
if {$word eq {}} continue
foreach {charset encoding word} [mime::word_decode $word] break
set word [encoding convertfrom $charset $word]
puts -nonewline $word
}
puts {}

Примечание: эквивалентный код на перле несложно найти в сети.

Несложно на тикле сделать парсер и для письма целиком:

package require mime

set message [exec cat /tmp/message.mbox]
set token [mime::initialize -string $message]
# set token [mime::initialize -string [read stdin]]

set content [mime::getproperty $token content] ;# text/plain
set body [mime::getbody $token] ;# veter, привет 17
set subject [mime::getheader $token subject] ;# {=?utf-8?B?0KLQtdGB0YLQvtCy0L7QtSDQvdC+0Lw=?==?utf-8?B?0LXRgCDRgNCw0Lc=?=}


См. также описание и примеры: Wiki Tcl/Tk: mime
И несколько полезных ссылок сс указанной вики-страницы:
Tcl MIME generates and parses MIME body parts.
Sorting mail into Maildirs
Mime File Attachment Extractor
Reading messages that might be MIME-encoded

Upd.
Протестируем скорость отправки письма со вложениями (для корректного указания заголовка From: нужно создать файл ~/.muttrc, как показано выше):

$ time echo "набор файлов"|mutt -s "Тестовое с файлами" -a /var/cache/apt/archives/elinks* -- pechnikov@mobigroup.ru
real 0m0.233s
user 0m0.136s
sys 0m0.040s

$ du -sh /var/cache/apt/archives/elinks*
464K /var/cache/apt/archives/elinks_0.11.4-3_i386.deb
644K /var/cache/apt/archives/elinks-data_0.11.4-3_all.deb

воскресенье, 3 января 2010 г.

И где же наши принципы

Недавно я упоминал уже Витуса Вагнера, вот и сегодня есть повод вспомнить один из его проектов Still Life. Сам автор говорит следующее:
StillLife - это принципиально новый (или хорошо забытый старый) подход к созданию интерактивных вебсайтов. Интерактивый веб-сайт без динамической генерации страниц.
В настоящее время в StillLife (почти) реализована функциональность форумного движка. В дальнейшем предполагается развить её до блогового движка, а может даже и до CMS общего назначения


Сразу признаюсь, что определенные "фи" технического характера я уже высказывал по поводу архитектуры упомянутого (хм, не знаю, как правильнее классифицировать - определение статический веб-сайт не соответствует задумке, а динамическим не является, ладно пусть просто будет стиллайф). Эти замечания (о пользе динамического формирования контента) остаются в силе, но к ним добавились новые, принципиальные соображения. И вот какие - для веб-проекта из разряда форумов и/или блогов одним из важнейших инструментов является полнотекстовый поиск. Несомненно, этот поиск можно выполнять средствами Google/Yahoo/etc., но таким образом не только мы как разработчики привязываемся к одному из упомянутых вендоров, но и своих пользователей "подсаживаем"! Допустимо ли это? Год назад я еще сомневался, а сегодня мой ответ - нет, не допустимо. Открытый проект должен пользоваться открытой системой поиска, которую можно скопировать на другой хост, получая резервную копию/распределение нагрузки/и проч. бонусы, а также гарантию, что проект не умрет однажды вместе со всей накопленной информацией - ведь всегда можно будет из сделанных "для себя" копий запустить заново или просто извлечь контент и/или предоставить доступ к нему.

Названные выше соображения заставляют подумать о своей системе поиска. Собственно, о технической реализации полнотекстового поиска я уже рассказывал, сегодня лишь даю ответ на вопрос: "а зачем это нужно?". Исходники будут выложены в системе контроля версий (fossil) здесь, deb-пакет в моем репозитории, адрес которого приведен тут. Обсуждаемая реализация обходится без каких-либо "конфигов" и состоит из единственного тиклевого скрипта-индексатора и набора фильтров для преобразования документов разных форматов в plain text. Как показала эксплуатация в некоторых коммерческих проектах, "хорошо приготовленный" plain text (сохраняющий таблицы и проч. форматирование) оказался оптимальным вариантом. Отмечу, что "по частям" исходники индексатора публиковались ранее в статьях с ярлыком FTS, но фильтры документов были представлены далеко не все.

Upd.

Имя пакета с системой поиска sqlite3-poisk, уже доступен в репозитории. Индексатор с фильтрами включены, утилиты для поиска по базе переписаны на C, но пока не выложены, т.к. тестируются. На днях обновлю пакет, добавив утилиты.

(C) Alexey Pechnikov aka MBG, mobigroup.ru