Всеволод Стахов. Заметки.

1Окт/090

ICQ транспорт и kqueue

При настройке icq транспорта (разумеется, имеется в виду py-icqt, так как остальные существующие сейчас проекты либо мертвы, как jit, либо не работают) на FreeBSD возникло странное желание использовать kqueue reactor, так как очевидно, что традиционные select/poll - не лучший выбор при сколько-нибудь большом числе persistent коннекций. Итак, вначале необходимо поставить devel/py-kqueue. Далее, если мы пропишем в конфигурационном файле

<reactor>kqueue</reactor>

то, скорее всего pyicqt начнет кушать 100% CPU. Если посмотреть ktrace, то можно будет увидеть следующее:

25821 python2.5 RET   kevent -1 errno 9 Bad file descriptor
25821 python2.5 CALL  gettimeofday(0xbfbfd8c8,0)
25821 python2.5 RET   gettimeofday 0
25821 python2.5 CALL  gettimeofday(0xbfbfd5b8,0)
25821 python2.5 RET   gettimeofday 0
25821 python2.5 CALL  write(0x2,0x28e70014,0x2ab)
25821 python2.5 GIO   fd 2 wrote 683 bytes
"Traceback (most recent call last):
File "/usr/local/lib/jabber/pyicq/PyICQt.py", line 16, in <module>
main.main()
File "/usr/local/lib/jabber/pyicq/src/main.py", line 473, in main
reactor.run()
File "/usr/local/lib/python2.5/site-packages/twisted/internet/base.py", line 1128, in run
self.mainLoop()
--- <exception caught here> ---
File "/usr/local/lib/python2.5/site-packages/twisted/internet/base.py", line 1140, in mainLoop
self.doIteration(t)
File "/usr/local/lib/python2.5/site-packages/twisted/internet/kqreactor.py", line 189, in doKEvent
l = self._kq.kevent([], len(self._selectables), timeout)
exceptions.OSError: [Errno 9] Bad file descriptor
"
Вспоминаем неприятную особенность kqueue: при форке не наследуюся kqueue дескрипторы. То есть, необходимо выполнить демонизацию самостоятельно. Вторая проблема - неверный расчет тайм-аутов. Фикс ее:

--- kqsyscallmodule.c.bak    2009-10-02 18:24:37.000000000 +0400
+++ kqsyscallmodule.c    2009-10-02 18:26:37.000000000 +0400
@@ -141,7 +141,7 @@
}

statichere PyTypeObject KQEvent_Type = {
-  PyObject_HEAD_INIT(NULL)
+  PyObject_HEAD_INIT(&PyType_Type)
0,                             // ob_size
"KQEvent",                     // tp_name
sizeof(KQEventObject),         // tp_basicsize
@@ -295,14 +295,14 @@

/* Build timespec for timeout */
totimespec.tv_sec = timeout / 1000;
-  totimespec.tv_nsec = (timeout % 1000) * 100000;
+  totimespec.tv_nsec = (timeout % 1000) * 1000000;

// printf("timespec: sec=%d nsec=%d\n", totimespec.tv_sec, totimespec.tv_nsec);

/* Make the call */
-
+  Py_BEGIN_ALLOW_THREADS
gotNumEvents = kevent (self->fd, changelist, haveNumEvents, triggered, wantNumEvents, &totimespec);
-
+  Py_END_ALLOW_THREADS
/* Don't need the input event list anymore, so get rid of it */
free (changelist);

@@ -365,7 +365,7 @@
statichere PyTypeObject KQueue_Type = {
/* The ob_type field must be initialized in the module init function
* to be portable to Windows without using C++. */
-    PyObject_HEAD_INIT(NULL)
+    PyObject_HEAD_INIT(&PyType_Type)
0,            /*ob_size*/
"KQueue",            /*tp_name*/
sizeof(KQueueObject),    /*tp_basicsize*/

Очевидно, что для получения наносекунд, нужно умножить миллисекунды на миллион, а не на сто тысяч.

Вторая проблема - корявая демонизация. Я решил ее исправлением стартового скрипта следующим образом:

--- /usr/local/etc/rc.d/jabber-pyicq-transport.orig    2009-10-02 18:57:06.000000000 +0400
+++ /usr/local/etc/rc.d/jabber-pyicq-transport    2009-10-02 19:29:00.000000000 +0400
@@ -24,8 +24,9 @@
: ${jabber_pyicq_user="ejabberd"}

pidfile="${jabber_pyicq_piddir}/PyICQt.pid"
-command_interpreter="/usr/local/bin/python2.5"
-command="${jabber_pyicq_dir}/PyICQt.py"
-command_args="-b -o pid=${pidfile}"
+python="/usr/local/bin/python2.5"
+procname="${python}"
+command="/usr/sbin/daemon"
+command_args="${python} ${jabber_pyicq_dir}/PyICQt.py -o pid=${pidfile}"

run_rc_command "$1"

То есть, демонизация происходит при помощи /usr/sbin/daemon. Проверка трейса работы pyicqt показала, что после этих действий для работы он использует kevent, причем, использует правильно. Хотя наличие select'ов все равно смущает, но для socket IO он все же использует kqueue.

Связано с категорией: Patches Нет комментариев
14Янв/092

Новая версия патча для поддержки exim-xclient

По пожеланиям ряда людей добавил поддержку нового nginx (0.7.x) в реализацию XCLIENT'а для exim'а. Отличие - понимание переданного параметра LOGIN и установка authenticated_id.
patch-exim-xclient

(Обновлено 03.02.2009, исправлен баг с неверным хранением строк, получаемых в команде xclient, спасибо Максиму Дунину)
Связано с категорией: Patches 2 Комментарии
15Фев/080

Race в коде закрытия unix сокета в FreeBSD 6.x

Выглядит race следующим образом (6.3-SMP):

 48976 nginx    1202926950.760500 CALL  recvfrom(0xad,0x1b2ab38,0x400,0,0,0)

 48976 nginx    1202926950.760511 GIO   fd 173 read 122 bytes

       "HTTP/1.0 200 OK\r

        Client-Host: [UNAVAILABLE]\r

        Auth-Status: OK\r

        Auth-Server: 10.8.2.19\r

        Auth-Port: 25\r

        Connection: close\r

        \r

       "

 48976 nginx    1202926950.760521 RET   recvfrom 122/0x7a

 48976 nginx    1202926950.760533 CALL  close(0xad)

 48976 nginx    1202926950.760548 RET   close -1 errno 57 Socket is not
 connected

Похоже, race возникает тут:

sys/kern/uipc_socket.c:

int soclose(so)

{

...

        if (so->so_state & SS_ISCONNECTED) {

                if ((so->so_state & SS_ISDISCONNECTING) == 0) {

                        error = sodisconnect(so);

}

int sodisconnect(so)

{

...

 if ((so->so_state & SS_ISCONNECTED) == 0)

                return (ENOTCONN);

}

so_state для unix сокета устанавливается следующим образом:

sys/kern/uipc_usrreq.c:

static void

unp_disconnect(struct unpcb *unp)

{

        struct unpcb *unp2 = unp->unp_conn;

        struct socket *so;

        UNP_LOCK_ASSERT();

        if (unp2 == NULL)

                return;

        unp->unp_conn = NULL;

        switch (unp->unp_socket->so_type) {

        case SOCK_DGRAM:

                LIST_REMOVE(unp, unp_reflink);

                so = unp->unp_socket;

                SOCK_LOCK(so);

                so->so_state &= ~SS_ISCONNECTED;

                SOCK_UNLOCK(so);

                break;

        case SOCK_STREAM:

                soisdisconnected(unp->unp_socket);

                unp2->unp_conn = NULL;

                soisdisconnected(unp2->unp_socket);

                break;

        }

}

Насколько я понял, для sock_stream закрываются оба конца соединения.
Закрываются так:

sys/kern/uipc_socket2.c:

void

soisdisconnected(so)

        register struct socket *so;

{

        /*

         * XXXRW: This code assumes that SOCK_LOCK(so) and

         * SOCKBUF_LOCK(&so->so_rcv) are the same.

         */

        SOCKBUF_LOCK(&so->so_rcv);

        so->so_state &=
~(SS_ISCONNECTING|SS_ISCONNECTED|SS_ISDISCONNECTING);

        so->so_state |= SS_ISDISCONNECTED;

        so->so_rcv.sb_state |= SBS_CANTRCVMORE;

        sorwakeup_locked(so);

        SOCKBUF_LOCK(&so->so_snd);

        so->so_snd.sb_state |= SBS_CANTSENDMORE;

        sbdrop_locked(&so->so_snd, so->so_snd.sb_cc);

        sowwakeup_locked(so);

        wakeup(&so->so_timeo);

}

То есть, не исключена ситуация, когда в функции soclose у одного из
сокетов состояние SS_ISCONNECTED, а в функции sodisconnect этот флаг уже
успевает убраться. Тогда может вернуться ENOTCONN. Как вариант фикса можно попробовать убрать проверку

 if ((so->so_state & SS_ISCONNECTED) == 0)

                return (ENOTCONN);

из функции sodisconnect, например, возвращая в этом месте 0.

Связано с категорией: Patches, Work Нет комментариев
13Дек/070

Memcached UDP fix

Следующий патчик исправляет ошибку в обработке udp запросов memcached'ом, которая приводила к падению его в корку при посылке пакета с неверным заголовком, а также не освобождала буфер чтения при ошибочной команде или же ненайденном ключе.
patch-memcached-udp

Связано с категорией: Patches, Work Нет комментариев
10Дек/072

XCLIENT в Exim

По просьбе Андрея Зверева написал патчик, позволяющий работу команды XCLIENT в exim 4. Описание этой команды можно найти тут. По умолчанию XCLIENT запрещен для всех хостов, но его можно включить, задав опцию xclient_allow_hosts в конфигурационном файле, например:

xclient_allow_hosts = 127.0.0.1 : 192.168.1.1

Пример SMTP диалога:
Connected to localhost.
Escape character is '^]'.
220 dhcp-ng2 ESMTP Exim 4.68 Mon, 10 Dec 2007 19:26:44 +0300
XCLIENT NAME=spike.porcupine.org ADDR=168.100.189.2 HELO=blah
220 XCLIENT success
MAIL FROM:<wietse@porcupine.org>
250 OK
RCPT TO:<user@example.com>
550 relay not permitted

patch-exim-xclient

Связано с категорией: Patches 2 Комментарии
23Ноя/072

Использование libmilter во FreeBSD

При сборке libmilter по умолчанию во FreeBSD она использует select(3) по умолчанию. Однако, в нагруженных системах это приводит к ошибке EBADF при попытке сделать select, так как номер дескриптора превышает FD_SETSIZE. В таком случае можно применить патч, который собирает libmilter с использованием poll(3).
libmilter.patch

Связано с категорией: Patches, Work 2 Комментарии
23Окт/073

unreal ircd + codepage patch

Существует достаточно много irc серверов, пригодных для установки в небольшой сети или же для корпоративного пользования, однако, если смотреть с точки зрения функциональности, интеграции с сервисами и поддержки кириллических кодировок выбор не так велик: тот же rusnet-ircd, присутствующий в портах не работает с ircservices, альтернативы которым в портах, по крайней мере, нет. Мой выбор пал на irc/unreal (http://www.unrealircd.com). Существует патч для этого сервера, позволяющий рекодировать irc протокол, используя iconv: http://irc.mgts.by/wiki/index.php/UnrealIRCd. Однако, у этого патча есть два больших недостатка: он не пригоден для использования внутри портов, и он не работает для amd64 (любой 64-х битной архитектуре) из-за неверного предположения о размере size_t. Я немного изменил патч, исправив эти недостатки.
patch-codepage

Связано с категорией: Patches, Work 3 Комментарии
23Окт/070

ng_fixttl – netgraph модуль, исправляющий ttl пакетов

Данный модуль создавался в целях обеспечения простенькой защиты от использования nat-роутеров пользователями сети. Естественно, обходится данная защита очень просто, но со своей задачей она-таки справляется. У нас ng_fixttl используется совместно с ng_ipfw:

ipfw add netgraph 2 ip from any to $host

При этом необходимо помнить о sysctl net.inet.ip.fw.one_pass, который определяет, будет ли правило netgraph эквивалентно allow, или же пакет будет возвращаться в ipfw (на следующее за правилом netgraph правило), или же сразу же будет отдаваться сетевому стеку (если эта переменная установлена в 1). 

ng_fixttl.tar.gz

Связано с категорией: FreeBSD, Patches, Work Нет комментариев