Модераторы: xvr

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Minidump в Linux 
:(
    Опции темы
ajuvolkov
Дата 25.2.2010, 13:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 13
Регистрация: 8.9.2009

Репутация: нет
Всего: нет



Всем привет.
Есть кроссплатформенное приложение, в котором нужно реализовать функцию создания отчетов при падении приложения.
Т.е. фактически на верхнем уровне перехватывать все пролезшие исключения, производить ряд тестов, делать минидамп памяти, засовывать все отчеты в архив и отправлять все это в техподдержку. Я знаю как это реализовать в Windows, даже уже сделал это, необходимо сделать что-то подобное для Linux, но я не знаю как делать minidump в Linux. В Windows это делается при помощи функции
MiniDumpWriteDump
А как в Linux?
Можно какой-нибудь пример?
Слышал, что в Linux система сама делает минидамп приложения, но тогда я не смогу сделать архив с отчетом и отправить все это в техподдержку, т.к. как я понимаю программа сначала аварийно завершится, после чего Linux сделает его minidump. 
Как быть? Подскажите, пожалуйста.
PM MAIL   Вверх
bilbobagginz
Дата 25.2.2010, 23:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Naughtius Maximus
****


Профиль
Группа: Экс. модератор
Сообщений: 8813
Регистрация: 2.3.2004
Где: Israel

Репутация: 4
Всего: 317



ajuvolkov, "при падении приложения" - широко сказано.
при опр. условиях система сама создает core dump. но надо осторожно с ним быть, и ограничить его через ulimit.
почитай на тему core в линуксе (через гугл), там описываются условия при к-рых файл создается.




--------------------
Я ещё не демон. Я только учусь.
PM WWW   Вверх
svlary
Дата 27.2.2010, 11:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 207
Регистрация: 8.9.2009

Репутация: 4
Всего: 4



Цитата

ограничить его через ulimit

   Например, вот так :
Код

//! Управление дампом памяти
/*! Разрешает или запрещает вывод дампа памяти при аварии.
 * 
 *  @param OnOff Разрешить / запретить дамп (0/1)
 *  @return  0 Все хорошо, -1 - ошибка
 */
int setCoreDump(int OnOff) {
    struct rlimit rl;
    int rc;
    
    rc = getrlimit(RLIMIT_CORE, &rl);
    if ( rc !=0 ) {
        syslog(LOG_ERR,"getrlimit error : %m");
        return -1;
    }
    if ( OnOff != 0 )
        rl.rlim_cur = rl.rlim_max;
    else
        rl.rlim_cur = 0;
    rc = setrlimit(RLIMIT_CORE, &rl);
    if ( rc !=0 ) {
        syslog(LOG_ERR,"setrlimit error : %m");
        return -1;
    }
     
    return 0;
}


PM MAIL   Вверх
ajuvolkov
Дата 3.3.2010, 10:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 13
Регистрация: 8.9.2009

Репутация: нет
Всего: нет



Спасибо за совет. Все вроде понятно, но только не ясны условия при которых создается дамп. 
Я с недавнего времени программирую под Linux, по этому еще плохо ориентируюсь в этой тематике.
В windows все просто - перехватил все пролезшие исключения на верхнем уровне (например в функции main), сделал дамп, тесты, запихнул все в архив, отправил в техподдержку информацию и закрыл приложение. 
А как быть в Linux? Если я таким же макаром перехвачу исключения, dump же не создастся?

Добавлено через 12 минут и 59 секунд
Добавлено @ 10:36
Цитата(ajuvolkov @ 3.3.2010,  10:25)
Спасибо за совет. Все вроде понятно, но только не ясны условия при которых создается дамп. 
Я с недавнего времени программирую под Linux, по этому еще плохо ориентируюсь в этой тематике.
В windows все просто - перехватил все пролезшие исключения на верхнем уровне (например в функции main), сделал дамп, тесты, запихнул все в архив, отправил в техподдержку информацию и закрыл приложение. 
А как быть в Linux? Если я таким же макаром перехвачу исключения, dump же не создастся?

Ааа, все кажется разобрался.
Нужно использовать функцию 
Код

#include <signal.h> 

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);


для сигналов 
SIGILL - Illegal Instruction 
SIGABRT - Abort signal from abort(3) 
SIGFPE  - Floating point exception    
SIGSEGV - Invalid memory reference
SIGSYS - Bad argument to routine (SVr4)
SIGTRAP - Trace/breakpoint trap
SIGXCPU - CPU time limit exceeded (4.2BSD) 
SIGXFSZ - File size limit exceeded (4.2BSD)

Я правильно понял?

Не понятно почему сигнал
SIGQUIT - Quit from keyboard

тоже вызывает создание минидампа? Это верно или я что - то не так понял из документации?

Это сообщение отредактировал(а) ajuvolkov - 3.3.2010, 10:38
PM MAIL   Вверх
svlary
Дата 4.3.2010, 07:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 207
Регистрация: 8.9.2009

Репутация: 4
Всего: 4



Цитата(ajuvolkov @  3.3.2010,  10:25 Найти цитируемый пост)
не ясны условия при которых создается дамп

В моем посте речь шла о дампе, который создается системой АВТОМАТИЧЕСКИ. Т.е. речь шла только о том, разрешить системе это делать, или запретить.

Цитата
Если я таким же макаром перехвачу исключения, dump же не создастся?

  Если Вы перехватили прерывание, то и обрабатывайте его! Иначе - зачем вы его перехватывли ?! Правда, есть способ, после своей обработки предать прерывание на стандартную... Но я не очень понимаю - зачем. Вы же вот что собираетесь делать :

Цитата
дамп, тесты, запихнул все в архив, отправил в техподдержку информацию и закрыл приложение.

Система этого делать не будет... Кроме дампа. 

Цитата

Ааа, все кажется разобрался. Нужно использовать функцию sigaction

  Ну, если разобрались - хорошо! Но дело это не сильно простое, поэтому приведу здесь пример,  как я это делаю у себя :

Код

//! Настройка обработки сигналов
/*!  Выполняет первоначальную настройку системы обработки
  * сигналов. После настройки все сигналы, приходящие от
  * клавиатуры и сигнал SIGPIPE игнорируются.
  * Для сигналов, связанных с тяжелыми ошибками, назначается
  * обработчик прерываний b_handler.
  * Сигналы таймера  обрабатываются t_handler если он задан.
  *
  *  @return true при успешном завершении, false при неудаче
 */
 bool handle_signals(void  (*b_handler)(int, siginfo_t*, void*), void  (*t_handler)(int, siginfo_t*, void*)) {
    sigset_t set;
    struct sigaction act;
    int rc;

     // На время обработки блокируем все сигналы
    rc = sigfillset(&set);
    rc = sigprocmask(SIG_SETMASK, &set, NULL);

    // Для всех сигналов, приходящих от клавиатуры, 
    // задаем игнорирование сигналов.
     memset(&act, 0, sizeof(act));
     rc = sigfillset(&act.sa_mask);
     act.sa_handler = SIG_IGN;
     rc = sigaction(SIGHUP, &act, NULL);
     rc = sigaction(SIGINT, &act, NULL);
     rc = sigaction(SIGQUIT, &act, NULL);
     rc = sigaction(SIGPIPE, &act, NULL);

    // Для всех остальных сигналов задаем 
    // обработчик сигнала - b_handler
     act.sa_sigaction = b_handler;
     act.sa_flags = SA_SIGINFO;
     rc = sigaction(SIGTERM, &act, NULL);
     rc = sigaction(SIGBUS, &act, NULL);
     rc = sigaction(SIGFPE, &act, NULL);
     rc = sigaction(SIGILL, &act, NULL);
     rc = sigaction(SIGSEGV, &act, NULL);
     rc = sigaction(SIGSYS, &act, NULL);
     rc = sigaction(SIGXCPU, &act, NULL);
     rc = sigaction(SIGXFSZ, &act, NULL);

    // Если задан обработчик таймера, то устанавливаем его
     if( t_handler != NULL ){
         act.sa_sigaction = t_handler;
         rc = sigaction(SIGALRM, &act, NULL);
     }
      
    // Снимаем блокировку с обработки сигналов
     rc = sigemptyset(&set);
     rc = sigprocmask(SIG_SETMASK, &set, NULL);
     
    return true;
}


    Два пояснения :
  •  Обработку кода завершения (rc) я опустил для краткости
  •  Эта функция использована не в пользовательской программе, а в демоне, поэтому все прерывания от клавиатуры (на всякий случай) отключены.
 b_handler печатет причину прервывания и пытается распечатать стек вызова функций с помощью функции :
Код

//-----------------------------------------------------------------
/*! Распечатывает стек вызова функций при аварийном завершении
 * процесса.
 *
 *  @param comment Текстовая константа, идентифицирующая процесс.
 *  @return  Ничего не возвращает.
 */
void print_trace (char *comment)
{
    void *array[100];
    size_t size;
    char **strings;
    size_t i;
    size = backtrace (array, 100);
    strings = backtrace_symbols (array, size);
    syslog(LOG_CRIT,"%s: Obtained %zd stack frames.", comment, size);
    for (i = 0; i < size; i++)
        syslog(LOG_CRIT,"%s: %s", comment, strings[i]);
    free (strings);
}


Про условия, при которых backtrace будет работать удовлетворительно - смотрите доку на Вашей системе.
PM MAIL   Вверх
MAKCim
Дата 4.3.2010, 10:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Воін дZэна
****


Профиль
Группа: Экс. модератор
Сообщений: 5644
Регистрация: 10.12.2005
Где: Менск, РБ

Репутация: 84
Всего: 207



svlary, 
для SIGSEGV лучше заюзать SA_ONSTACK и статический буфер
в первом sigprocmask  лучше сохранить текущую маску и восстановить ее в последнем


--------------------
Ах, у елі, ах, у ёлкі, ах, у елі злыя волкі ©

PM MAIL   Вверх
ajuvolkov
Дата 4.3.2010, 15:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 13
Регистрация: 8.9.2009

Репутация: нет
Всего: нет



Спасибо за помощь, я уже близок к цели ))))))))

svlary:
Цитата

В моем посте речь шла о дампе, который создается системой АВТОМАТИЧЕСКИ. Т.е. речь шла только о том, разрешить системе это делать, или запретить.
Цитата

Если я таким же макаром перехвачу исключения, dump же не создастся?


  Если Вы перехватили прерывание, то и обрабатывайте его! Иначе - зачем вы его перехватывли ?! Правда, есть способ, после своей обработки предать прерывание на стандартную... Но я не очень понимаю - зачем. Вы же вот что собираетесь делать :

Цитата

дамп, тесты, запихнул все в архив, отправил в техподдержку информацию и закрыл приложение.


Система этого делать не будет... Кроме дампа.


Все это мне нужно для того, чтобы реализовать в приложении механизм отправки отчета об ошибках к нам в компанию. Т.е. если приложение валится у клиента, то выводится сообщение, на подобии сообщений о проблемах в приложениях Microsoft. Мол что произошла критическая ошибка, приложение будет закрыто, отправить информацию о ошибке в компанию XXXXXXXX?
Если пользователь жмет ок, то программа выполняет некоторые тесты, формирует отчет, в котором находятся результаты этих тестов и дамп памяти, записывает все это в архив, соединяется с почтовым сервером компании по протоколу SMTP и отсылает туда этот архив. Все это уже работает под Windows. Осталось сделать под Linux. Впринципе, функции формирования отчета и отправки по почте работают у меня и под Linux. Задача состоит в том как мне сделать dump.

Вырезка из кода:

Код

// Управление дампом памяти в Linux
// Разрешает или запрещает вывод дампа памяти при аварии.
// OnOff Разрешить / запретить дамп (1/0)
// return  true Все хорошо, - false - ошибка

bool setCoreDump(int OnOff) {
    struct rlimit rl;
    int rc;
    
    rc = getrlimit(RLIMIT_CORE, &rl);
    if ( rc !=0 ) {
        //syslog(LOG_ERR,"getrlimit error : %m");
        return false;
    }
    if ( OnOff != 0 )
        rl.rlim_cur = rl.rlim_max;
    else
        rl.rlim_cur = 0;
    rc = setrlimit(RLIMIT_CORE, &rl);
    if ( rc !=0 ) {
        //syslog(LOG_ERR,"setrlimit error : %m");
        return false;
    }
     
    return true;
}

// Функция, которая будет вызвана системой, в случае аварийной ситуации в приложении
void on_coredump(int signal)
{
// Тут идет создание отчета и отправка по почте
}

// Инициализация
void ProblemInformer::initialize()
{
#ifdef Q_WS_WIN
        SetUnhandledExceptionFilter(WriteMiniDump);
#elif defined(Q_WS_X11) || defined(Q_WS_QWS)
    if (setCoreDump(1))
    {
         struct sigaction act;

         act.sa_handler     = on_coredump;
                        sigemptyset(&act.sa_mask);
                        act.sa_flags = 0;

                        // Назначаем обработчик события аварийной ситуации сигналам Linux
            
            if(sigaction(SIGILL,         &act, NULL) == -1 ||    // SIGILL - Illegal Instruction
               sigaction(SIGABRT,   &act, NULL) == -1 ||    // SIGABRT - Abort signal from abort
               sigaction(SIGFPE,         &act, NULL) == -1 ||    // SIGFPE  - Floating point exception   
               sigaction(SIGSEGV,   &act, NULL) == -1 ||    // SIGSEGV - Invalid memory reference
               sigaction(SIGSYS,         &act, NULL) == -1 ||    // SIGSYS - Bad argument to routine (SVr4)
               sigaction(SIGTRAP,   &act, NULL) == -1 ||    // SIGTRAP - Trace/breakpoint trap
               sigaction(SIGXCPU,  &act, NULL) == -1 ||    // SIGXCPU - CPU time limit exceeded (4.2BSD)
               sigaction(SIGXFSZ,   &act, NULL) == -1 )    // SIGXFSZ - File size limit exceeded (4.2BSD)
            {
                #ifdef _DEBUG
                    QMessageBox::critical(NULL, QString("Error..."),  QString("ProblemInformer::initialize(): sigaction failure"));
                #endif
            }
    }
    #ifdef _DEBUG
    else
    {
        QMessageBox::critical(NULL, QString("Error..."),  QString("ProblemInformer::initialize(): setCoreDump failure"));
    }
    #endif
#endif
}



При возникновении критической ситуации вызывается on_coredump, в этой функции проводится ряд тестов и формируется отчет, но к этому моменту дампа памяти еще нет. Как его создать? 
Т.е. я уже знаю что приложение на грани фола, и мне нужен дамп, чтобы заказчик отправил мне его по почте и я смог у себя на работе найти, где проблема, изучая этот дам.
Вот для этого все это мне нужно.

Пробовал сам принудительно рушить приложение путем простого деления на ноль. Функция  on_coredump вызывается. Но она вызывается постоянно. Т.е. как только она отработала, она вызывается снова и, к стати, файла дампа нет в папке с приложением. Дамп, как я понимаю, создастся, когда приложение окончательно рухнет?

Теперь я понятно изложил проблему? Или еще не до конца ясно, что я хочу реализовать?
PM MAIL   Вверх
svlary
Дата 5.3.2010, 07:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 207
Регистрация: 8.9.2009

Репутация: 4
Всего: 4



Цитата(MAKCim @  4.3.2010,  10:23 Найти цитируемый пост)
лучше заюзать
 - с замечаниями полностью согласен, но... Лучшее - враг Хорошего!  smile

PM MAIL   Вверх
svlary
Дата 5.3.2010, 08:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 207
Регистрация: 8.9.2009

Репутация: 4
Всего: 4



Цитата(ajuvolkov @  4.3.2010,  15:29 Найти цитируемый пост)
Или еще не до конца ясно, что я хочу реализовать? 
 - Ну почему же?! Вполне ясно.  smile  В Fedora-12 эта штука называется "The tool to help non-power users with bug reporting, making it as easy as a few mouse clicks. This version has a lot of improvements and fixes and is more user friendly." А сам пакет называется ABRT 1.0  (abrt-1.0.7-1.fc12.i686). К нему есть соответствующая библиотека, плюгины для багзиллы, логгеров, обертки для питона и еще что-то... Есть готовый GUI... smile Подробности можно посмотреть вот здесь : Abrt 1.0
PM MAIL   Вверх
ajuvolkov
Дата 5.3.2010, 10:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 13
Регистрация: 8.9.2009

Репутация: нет
Всего: нет



Цитата(svlary @ 5.3.2010,  08:26)
Цитата(ajuvolkov @  4.3.2010,  15:29 Найти цитируемый пост)
Или еще не до конца ясно, что я хочу реализовать? 
 - Ну почему же?! Вполне ясно.  smile  В Fedora-12 эта штука называется "The tool to help non-power users with bug reporting, making it as easy as a few mouse clicks. This version has a lot of improvements and fixes and is more user friendly." А сам пакет называется ABRT 1.0  (abrt-1.0.7-1.fc12.i686). К нему есть соответствующая библиотека, плюгины для багзиллы, логгеров, обертки для питона и еще что-то... Есть готовый GUI... smile Подробности можно посмотреть вот здесь : Abrt 1.0

Да хочется свое, тем более что уже почти все готово =) Готов и работает все, кроме сохранения дампа в Линукс. В Windows все уже хорошо работает
PM MAIL   Вверх
MAKCim
Дата 5.3.2010, 11:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Воін дZэна
****


Профиль
Группа: Экс. модератор
Сообщений: 5644
Регистрация: 10.12.2005
Где: Менск, РБ

Репутация: 84
Всего: 207



Цитата(ajuvolkov @  4.3.2010,  15:29 Найти цитируемый пост)
Теперь я понятно изложил проблему? Или еще не до конца ясно, что я хочу реализовать? 

просто поставь SIG_DFL на SIGSEGV в обработчике и создай новый процесс
далее дефолтный обработчик libc вернет управление на инструкцию, вызвавшую исключение
процесс завершится с созданием корки, из дочернего процесса это отслеживается
как создастся корка, можем делать с ней, все что угодно

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


--------------------
Ах, у елі, ах, у ёлкі, ах, у елі злыя волкі ©

PM MAIL   Вверх
ajuvolkov
Дата 5.3.2010, 15:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 13
Регистрация: 8.9.2009

Репутация: нет
Всего: нет



Цитата(MAKCim @ 5.3.2010,  11:35)
Цитата(ajuvolkov @  4.3.2010,  15:29 Найти цитируемый пост)
Теперь я понятно изложил проблему? Или еще не до конца ясно, что я хочу реализовать? 

просто поставь SIG_DFL на SIGSEGV в обработчике и создай новый процесс
далее дефолтный обработчик libc вернет управление на инструкцию, вызвавшую исключение
процесс завершится с созданием корки, из дочернего процесса это отслеживается
как создастся корка, можем делать с ней, все что угодно

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

Спасибо за помощь. 
Попробую реализовать.
PM MAIL   Вверх
ajuvolkov
Дата 10.3.2010, 10:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 13
Регистрация: 8.9.2009

Репутация: нет
Всего: нет



Цитата(ajuvolkov @ 5.3.2010,  15:14)
Цитата(MAKCim @ 5.3.2010,  11:35)
Цитата(ajuvolkov @  4.3.2010,  15:29 Найти цитируемый пост)
Теперь я понятно изложил проблему? Или еще не до конца ясно, что я хочу реализовать? 

просто поставь SIG_DFL на SIGSEGV в обработчике и создай новый процесс
далее дефолтный обработчик libc вернет управление на инструкцию, вызвавшую исключение
процесс завершится с созданием корки, из дочернего процесса это отслеживается
как создастся корка, можем делать с ней, все что угодно

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

Спасибо за помощь. 
Попробую реализовать.

Что-то я не очень понимаю каким образом отловить момент создания корки в дочернем процессе =(
PM MAIL   Вверх
MAKCim
Дата 10.3.2010, 11:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Воін дZэна
****


Профиль
Группа: Экс. модератор
Сообщений: 5644
Регистрация: 10.12.2005
Где: Менск, РБ

Репутация: 84
Всего: 207



Цитата(ajuvolkov @  10.3.2010,  10:53 Найти цитируемый пост)
Что-то я не очень понимаю каким образом отловить момент создания корки в дочернем процессе =( 

WCOREDUMP(status)
man 2 wait


--------------------
Ах, у елі, ах, у ёлкі, ах, у елі злыя волкі ©

PM MAIL   Вверх
ajuvolkov
Дата 10.3.2010, 11:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 13
Регистрация: 8.9.2009

Репутация: нет
Всего: нет



Цитата(MAKCim @ 10.3.2010,  11:26)
Цитата(ajuvolkov @  10.3.2010,  10:53 Найти цитируемый пост)
Что-то я не очень понимаю каким образом отловить момент создания корки в дочернем процессе =( 

WCOREDUMP(status)
man 2 wait

Спасибо, уже нашел отличную статью в Linux Format Magazine
Кому интересно, ее можно прочесть здесь

http://www.scribd.com/doc/2248668/Linux-Format-Magazine-85

страницы 100-103

Это сообщение отредактировал(а) ajuvolkov - 10.3.2010, 11:58
PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С/С++: Программирование под Unix/Linux"
xvr
  • Проставьте несколько ключевых слов темы, чтобы её можно было легче найти.
  • Не забывайте пользоваться кнопкой "Код".
  • Вопросы мобильной разработки тут
  • Телепатов на форуме нет! Задавайте чёткий, конкретный и полный вопрос. Указывайте полностью ошибки компилятора и компоновщика.
  • Новое сообщение должно иметь прямое отношение к разделу форума. Флуд, флейм, оффтопик запрещены.
  • Категорически запрещается обсуждение вареза, "кряков", взлома программ и т.д.

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, xvr.

 
 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Программирование под Unix/Linux | Следующая тема »


 




[ Время генерации скрипта: 0.0629 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.