Предупреждение: я совсем не знаком с C, но недавно прочитал слайд, на который напрямую отвечает Джим Мейринг, сотрудник RedHat и сопровождающий GNUlib: https://www.gnu.org/ghm/2011/paris/slides/jim-meyering-goodbye-world.pdf. Я просто суммирую.
TL; DR
Получите closeout.c и его зависимости от GNUlib в ваш источник и вызовите
atexit(close_stdout);
как ваша первая строка в main.
Краткое описание
Сначала несколько предупреждений, цитируя POSIX :
Поскольку после вызова fclose () любое использование потока приводит к неопределенному поведению, fclose () не следует использовать в stdin, stdout или stderr, за исключением непосредственно перед завершением процесса, ...... Если есть какие-либо Обработчики atexit (), зарегистрированные приложением, такого вызова fclose () не должны происходить, пока не завершится последний обработчик. После того как fclose () была использована для закрытия stdin, stdout или stderr, стандартного способа повторно открыть любой из этих потоков не существует.
За использованием close () в файловых дескрипторах STDIN_FILENO, STDOUT_FILENO или STDERR_FILENO следует немедленно выполнить операцию для повторного открытия этих файловых дескрипторов. ...... Кроме того, close (), за которым следует операция повторного открытия (например, open (), dup () и т. Д.), Не является атомарной; dup2 () должен использоваться для изменения стандартных файловых дескрипторов.
Закрытие потока без обработки его ошибок не является надежным, и то же самое для stdout и stderr. Вот список ошибок, которые вам нужно устранить:
fclose(stdout)
ferror(stdout)
a.k.a. предыдущая ошибка
__fpending(stdout)
a.k.a. материал не очищен
Обработка этих ошибок, как GNUlib реализует в close-stream.c , приведена ниже.
int
close_stream (FILE *stream)
{
const bool some_pending = (__fpending (stream) != 0);
const bool prev_fail = (ferror (stream) != 0);
const bool fclose_fail = (fclose (stream) != 0);
/* Return an error indication if there was a previous failure or if
fclose failed, with one exception: ignore an fclose failure if
there was no previous error, no data remains to be flushed, and
fclose failed with EBADF. That can happen when a program like cp
is invoked like this 'cp a b >&-' (i.e., with standard output
closed) and doesn't generate any output (hence no previous error
and nothing to be flushed). */
if (prev_fail || (fclose_fail && (some_pending || errno != EBADF)))
{
if (! fclose_fail)
errno = 0;
return EOF;
}
return 0;
}
Примечание: __fpending
специально для glibc и может не быть переносимым. OTOH, это на пути к стандартизации как fpending
.
P.S:.
Я просто хотел направить вывод stdout и stderr в файл журнала вместо консоли.
Это не очень хорошая причина закрывать stdout и stderr, если вы пишете демон в соответствии с http://cloud9.hedgee.com./scribbles/daemon#logging. Вы должны позволить менеджеру демона (например, инструменты демона, runit, s6, nosh, OpenRC и systemd) справиться с перенаправлением.
Тем не менее, вы все равно должны закрыть любой поток, в который программа когда-либо записывала в конце, чтобы проверить ошибки. Цитата из close-stream.c:
Если программа записывает что-нибудь в STREAM, эта программа должна закрыться
ПОТОК и убедитесь, что это успешно, прежде чем выйти. Иначе,
Предположим, что вы идете до крайности проверки статуса возврата
каждой функции, которая делает явную запись в STREAM. Последний
printf может преуспеть в записи во внутренний буфер потока, и все же
fclose (STREAM) может все еще потерпеть неудачу (например, из-за ошибки переполнения диска)
когда он пытается записать эти буферизованные данные. Таким образом, вы бы
осталось с неполным выходным файлом и программа-нарушитель
выйти успешно. Даже вызова fflush не всегда достаточно,
поскольку некоторые файловые системы (NFS и CODA) буферизуют записанные / очищенные данные
до фактического закрытия вызова.
Кроме того, расточительно проверять возвращаемое значение при каждом вызове
что пишет в STREAM - просто пусть запись состояния внутреннего потока
провал. Это то, что тест на ужасы проверяет ниже.