Hi ALL!
Есть две реализации запуска демона. Основное различие состоит в том, что в первой версии я сначала форкаю новый процесс, а затем создаю сокеты, во втором наоборот (сначала сокеты, а затем fork). В первом варианте все работает как нужно, во втором проблемы. Функция select в MainLoop постоянно возвращает 2, что указывает на наличие данных в двух сокетах (я использую UNIX сокет и TCP), хотя реально ничего еще нет. Я подозреваю, что проблема именно в последовательности вызова fork и socket. У меня закрытие сокетов сделано в деструкторе, поэтому после завершения главного процесса выполняется закрытие его сокетов. Не может ли быть так, что информация о сокетах не копируется, и при закрытии сокетов из главного процесса, они перестают быть доступны из порожденного?
Для меня второй вариант предпочтительней, т.к. в первом случае я не могу в скрипте запуска отследить запущен ли демон в действительности, если ошибки возникли на этапе создания сокетов и объекта TDataInterface. В данном случае скрипт получает код возврата EXIT_SUCCESS при завершении главного процесса.
Как поступить, чтобы и волки были сыти и овцы целы?
Заранее благодарю за ответы!
Версия 1:
int Server::TServerApplication::Run(int _argc, char ** _argv)
{
// проверяем наличие обязательных параметров в коммандной строке
if(_argc < 2 || _argv == 0)
{
char *fileName = strrchr(_argv[0], '/');
if(fileName == NULL)
fileName = _argv[0];
else
fileName++;
Usage(fileName);
return EXIT_FAILURE;
}
else
if(!ParseCMD(_argc, _argv))
return EXIT_FAILURE;
// стартуем как демон
if(startAsDaemon)
{
if(!errorFile.is_open())
{
cerr << "Не определен параметр ERROR_FILE или произошла ошибка при открытии файла\n";
return EXIT_FAILURE;
}
if(!logFile.is_open())
{
cerr << "Не определен параметр LOG_FILE или произошла ошибка при открытии файла\n";
return EXIT_FAILURE;
}
// переход на корневой раздел,
chdir("/");
// создаем копию процесса и завершаем родительский процесс
if((pid = fork()))
return EXIT_SUCCESS;
// отрываемся от управляющего терминала
setsid();
}
// устанавливаем обработчик handler на некоторые сигналы
for(int j = 1; j < 32; j++)
switch(j)
{
// по стандарту POSIX некоторые сигналы желательно обрабатывать всегда (не игнорировать)
// назначим для этих сигналов обработчик по умолчанию
case SIGFPE:
case SIGILL:
case SIGSEGV:
sigset(j, SIG_DFL);
break;
default:
sigset(j, handler);
}
{ // сохраняем PID в файл
ofstream pid_file(pidFile.c_str(), ios::out);
if(pid_file.is_open())
{
pid_file << getpid();
pid_file.close();
}
}
// перенаправляем потоки в режиме демона
if(startAsDaemon)
{
cerr.rdbuf(errorFile.rdbuf());
cout.rdbuf(logFile.rdbuf());
}
try
{
int rc;
factory = new TThreadFactory(this);
// загружаем данные из БД
dataIFACE = new TDataInterface(this, true);
sock = new TSocket(PF_UNIX, SOCK_STREAM);
// проверяем статус создания UNIX сокета
if(sock->Good())
{
// присваиваем имя сокету и делаем сокет слушающим
if((rc = sock->Bind(socketName.c_str())) < 0 || (!rc && sock->Listen() < 0))
return EXIT_FAILURE;
}
else
return EXIT_FAILURE;
tcp_sock = new TSocket(PF_INET, SOCK_STREAM);
// проверяем статус создания TCP сокета
if(tcp_sock->Good())
{
rc = 1;
setsockopt(tcp_sock->SID(), SOL_SOCKET, SO_REUSEADDR, &rc, sizeof(rc));
// присваиваем имя сокету и делаем сокет слушающим
if((rc = tcp_sock->Bind(serverName.c_str(), serverPort)) < 0 || (!rc && tcp_sock->Listen() < 0))
return EXIT_FAILURE;
}
else
return EXIT_FAILURE;
}
catch(int) // ошибка создания TDataInterface
{
return EXIT_FAILURE;
}
catch(std::bad_alloc)
{
PrintCurrentTime();
cerr << "TServerApplication::Run(): Ошибка выделения памяти\n";
return EXIT_FAILURE;
}
PrintCurrentTime(cout);
cout << "Сервер успешно стартовал\n\n";
// запускаем главный цикл приложения
return MainLoop();
}
Версия 2:
int Server::TServerApplication::Run(int _argc, char ** _argv)
{
// проверяем наличие обязательных параметров в коммандной строке
if(_argc < 2 || _argv == 0)
{
char *fileName = strrchr(_argv[0], '/');
if(fileName == NULL)
fileName = _argv[0];
else
fileName++;
Usage(fileName);
return EXIT_FAILURE;
}
else
if(!ParseCMD(_argc, _argv))
return EXIT_FAILURE;
// стартуем как демон
if(startAsDaemon)
{
if(!errorFile.is_open())
{
cerr << "Не определен параметр ERROR_FILE или произошла ошибка при открытии файла\n";
return EXIT_FAILURE;
}
if(!logFile.is_open())
{
cerr << "Не определен параметр LOG_FILE или произошла ошибка при открытии файла\n";
return EXIT_FAILURE;
}
// перенаправляем потоки в режиме демона
cerr.rdbuf(errorFile.rdbuf());
cout.rdbuf(logFile.rdbuf());
}
try
{
int rc;
factory = new TThreadFactory(this);
// загружаем данные из БД
dataIFACE = new TDataInterface(this, true);
sock = new TSocket(PF_UNIX, SOCK_STREAM);
// проверяем статус создания UNIX сокета
if(sock->Good())
{
// присваиваем имя сокету и делаем сокет слушающим
if((rc = sock->Bind(socketName.c_str())) < 0 || (!rc && sock->Listen() < 0))
return EXIT_FAILURE;
}
else
return EXIT_FAILURE;
tcp_sock = new TSocket(PF_INET, SOCK_STREAM);
// проверяем статус создания TCP сокета
if(tcp_sock->Good())
{
rc = 1;
setsockopt(tcp_sock->SID(), SOL_SOCKET, SO_REUSEADDR, &rc, sizeof(rc));
// присваиваем имя сокету и делаем сокет слушающим
if((rc = tcp_sock->Bind(serverName.c_str(), serverPort)) < 0 || (!rc && tcp_sock->Listen() < 0))
return EXIT_FAILURE;
}
else
return EXIT_FAILURE;
}
catch(int) // ошибка создания TDataInterface
{
return EXIT_FAILURE;
}
catch(std::bad_alloc)
{
PrintCurrentTime();
cerr << "TServerApplication::Run(): Ошибка выделения памяти\n";
return EXIT_FAILURE;
}
PrintCurrentTime(cout);
cout << "Сервер успешно стартовал\n\n";
if(startAsDaemon)
{
// переход на корневой раздел,
chdir("/");
// создаем копию процесса и завершаем родительский процесс
if((pid = fork()))
return EXIT_SUCCESS;
// отрываемся от управляющего терминала
setsid();
}
// устанавливаем обработчик handler на некоторые сигналы
for(int j = 1; j < 32; j++)
switch(j)
{
// по стандарту POSIX некоторые сигналы желательно обрабатывать всегда (не игнорировать)
// назначим для этих сигналов обработчик по умолчанию
case SIGFPE:
case SIGILL:
case SIGSEGV:
sigset(j, SIG_DFL);
break;
default:
sigset(j, handler);
}
{ // сохраняем PID в файл
ofstream pid_file(pidFile.c_str(), ios::out);
if(pid_file.is_open())
{
pid_file << getpid();
pid_file.close();
}
}
// запускаем главный цикл приложения
return MainLoop();
}