Во время работы в "Рамблере" я написал несколько вещей, которые могут быть полезны не только у нас, но и пригодиться другим. Поэтому я решил выложить их в виде mercurial репозитария. Среди них:
- http://cebka.pp.ru/hg/rmilter/ - фильтр почты для sendmail/postfix, позволяющий делать различные проверки, в частности, spamassassin/clamav/dcc/spf, также имеются встроенные средства для грейлистинга и проверки лимитов (через memcached), также поддерживаются проверки по регэкспам. Мильтр конфигурируется при помощи конфигурационного файла, снабженного комментариями и описанного в странице man (rmilter.conf.5)
- http://cebka.pp.ru/hg/nginx-smtp-policy/ - асинхронный резолвер имен (dns resolver) для работы с nginx с патчем, изначально написанным Максом Дуниным (http://mdounin.ru/). Также nginx-smtp-policy осуществляет load balancing по различным mx'ам (согласно весам) и отсеивание нежелательной почты путем проверки RBL и мониторинг mx'ов, на которые осуществляется транспорт почты (nginx-smtp-watchdog). Данная программа предназначена для почтовых систем, обслуживающих очень большой поток почты и существенно снижает нагрузку на конечные mx'ы (патч, который есть в репозитории, работает с nginx до версии 0.6.11 включительно).
- http://cebka.pp.ru/hg/libevent/ - патчи, необходимые для работы nginx-smtp-policy. Существенно ускоряют резолвинг за счет использования вместо линейного списка DNS запросов хеш таблицу, а также патч, позволяющий резолвить TXT записи (для проверки RBL).
В настоящее время я работаю над созданием быстрой альтернативы spamassassin'у, если данный проект получится удачным, то он тоже будет открыт, скорее всего.
Tuesday, August 12, 2008
Thursday, June 5, 2008
Использование dmalloc
Так как использовать dmalloc не так просто, то записать основные принципы на будущее не бесполезно. Итак, dmalloc линкуется статически, например, так:
.ifdef DMALLOC
CFLAGS+= -DDMALLOC -DDMALLOC_FUNC_CHECK
LIBS+= -ldmalloc
.endif
Далее, самый простой способ использовать библиотеку:
определяем функции malloc и free, чтобы иметь представление, из какой строчки кода они вызываются:
#ifdef DMALLOC
#include <dmalloc.h>
#define mymalloc(x) dmalloc_malloc(__FILE__, __LINE__, (x), DMALLOC_FUNC_MALLOC, 0, 1)
#define myrealloc(x, y) dmalloc_realloc(__FILE__, __LINE__, (x), (y), DMALLOC_FUNC_REALLOC, 0, 1)
#define myfree(x) dmalloc_free(__FILE__, __LINE__, (x), DMALLOC_FUNC_FREE)
#define mystrdup(x) dmalloc_strndup(__FILE__, __LINE__, (x), strlen((x)), 1)
#else
#define mymalloc(x) malloc((x))
#define myrealloc(x, y) realloc((x), (y))
#define myfree(x) free((x))
#define mystrdup(x) strdup((x))
#endif
Далее в коде пользуемся только этими функциями для работы с памятью.
Для инициализации библиотеки можно пользоваться переменными окружения. Но в ряде случаев это не работает (cgi, su и прочее). Поэтому я решил делать инициализацию так:
#ifdef DMALLOC
char dmalloc_log[PATH_MAX];
snprintf (dmalloc_log, sizeof (dmalloc_log), "debug=0x4f47d03,log=%s/dmalloc-%%p.log", DMALLOC_LOG_PATH);
dmalloc_debug_setup (dmalloc_log);
#endif
Лог файлы создаются автоматически, DMALLOC_LOG_PATH - это каталог, куда должна быть разрешена запись пользователю, из-под которого запускается интересующая нас программа.
Логирование неосвобожденной памяти тоже лучше выполнять самому, чтобы потом не бояться потерять информацию при падении программы где-то. На мой взгляд, лучше всего использовать марки:
в начале секции кода, где может утекать память ставим:
#ifdef DMALLOC
unsigned int mark;
mark = dmalloc_mark();
#endif
в конце секции:
#ifdef DMALLOC
dmalloc_log_changed(mark,
1 /* log unfreed pointers */,
0 /* do not log freed pointers */,
1 /* log each pnt otherwise summary */);
#endif
В логе имеем что-то вида
1212677815: 289299: Dumping Not-Freed Pointers Changed Since Mark 289178:
1212677815: 289299: memory table is empty
Или же указатели на память:
1212677788: 288383: not freed: '0x80144af88|s1' (24 bytes) from 'unknown'
1212677788: 288383: not freed: '0x80144afc8|s5' (40 bytes) from 'unknown'
1212677788: 288383: not freed: '0x80144be88|s1' (24 bytes) from 'unknown'
1212677788: 288383: not freed: '0x80144bec8|s1' (40 bytes) from 'unknown'
Можно уронить процесс в корку (kill -SEGV, например) и посмотреть эти адреса в gdb:
(gdb) x/40c 0x80144bec8
.ifdef DMALLOC
CFLAGS+= -DDMALLOC -DDMALLOC_FUNC_CHECK
LIBS+= -ldmalloc
.endif
Далее, самый простой способ использовать библиотеку:
определяем функции malloc и free, чтобы иметь представление, из какой строчки кода они вызываются:
#ifdef DMALLOC
#include <dmalloc.h>
#define mymalloc(x) dmalloc_malloc(__FILE__, __LINE__, (x), DMALLOC_FUNC_MALLOC, 0, 1)
#define myrealloc(x, y) dmalloc_realloc(__FILE__, __LINE__, (x), (y), DMALLOC_FUNC_REALLOC, 0, 1)
#define myfree(x) dmalloc_free(__FILE__, __LINE__, (x), DMALLOC_FUNC_FREE)
#define mystrdup(x) dmalloc_strndup(__FILE__, __LINE__, (x), strlen((x)), 1)
#else
#define mymalloc(x) malloc((x))
#define myrealloc(x, y) realloc((x), (y))
#define myfree(x) free((x))
#define mystrdup(x) strdup((x))
#endif
Далее в коде пользуемся только этими функциями для работы с памятью.
Для инициализации библиотеки можно пользоваться переменными окружения. Но в ряде случаев это не работает (cgi, su и прочее). Поэтому я решил делать инициализацию так:
#ifdef DMALLOC
char dmalloc_log[PATH_MAX];
snprintf (dmalloc_log, sizeof (dmalloc_log), "debug=0x4f47d03,log=%s/dmalloc-%%p.log", DMALLOC_LOG_PATH);
dmalloc_debug_setup (dmalloc_log);
#endif
Лог файлы создаются автоматически, DMALLOC_LOG_PATH - это каталог, куда должна быть разрешена запись пользователю, из-под которого запускается интересующая нас программа.
Логирование неосвобожденной памяти тоже лучше выполнять самому, чтобы потом не бояться потерять информацию при падении программы где-то. На мой взгляд, лучше всего использовать марки:
в начале секции кода, где может утекать память ставим:
#ifdef DMALLOC
unsigned int mark;
mark = dmalloc_mark();
#endif
в конце секции:
#ifdef DMALLOC
dmalloc_log_changed(mark,
1 /* log unfreed pointers */,
0 /* do not log freed pointers */,
1 /* log each pnt otherwise summary */);
#endif
В логе имеем что-то вида
1212677815: 289299: Dumping Not-Freed Pointers Changed Since Mark 289178:
1212677815: 289299: memory table is empty
Или же указатели на память:
1212677788: 288383: not freed: '0x80144af88|s1' (24 bytes) from 'unknown'
1212677788: 288383: not freed: '0x80144afc8|s5' (40 bytes) from 'unknown'
1212677788: 288383: not freed: '0x80144be88|s1' (24 bytes) from 'unknown'
1212677788: 288383: not freed: '0x80144bec8|s1' (40 bytes) from 'unknown'
Можно уронить процесс в корку (kill -SEGV, например) и посмотреть эти адреса в gdb:
(gdb) x/40c 0x80144bec8
Friday, February 15, 2008
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.
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.
Tuesday, February 5, 2008
Миграция с jabberd2 на openfire
Используя плагин экспорта/импорта у openfire можно заводить
пользователей, используя достаточно простой xml код. Хотя этот плагин
работает достаточно нестабильно (xml файл, содержащий 4к пользователей
он не переварил, вываливаясь с NullPointerException), по крайней мере у
меня, но, возможно, кому-то это окажется полезным.
jabber_to_xml.pl
пользователей, используя достаточно простой xml код. Хотя этот плагин
работает достаточно нестабильно (xml файл, содержащий 4к пользователей
он не переварил, вываливаясь с NullPointerException), по крайней мере у
меня, но, возможно, кому-то это окажется полезным.
jabber_to_xml.pl
Subscribe to:
Posts (Atom)