「オンデマンド」で出力されたプログラムをログに記録したい。例えば。出力は端末に記録されますが、別のプロセスがいつでも現在の出力をフックできます。
古典的な方法は次のようになります:
myprogram 2>&1 | tee /tmp/mylog
オンデマンドで
tail /tmp/mylog
ただし、これにより、ドライブの空き容量がなくなるまで使用しなくても、ログファイルは増加し続けます。だから私の試みは:
mkfifo /tmp/mylog
myprogram 2>&1 | tee /tmp/mylog
オンデマンドで
cat /tmp/mylog
これで、いつでも/ tmp/mylogを読み取ることができます。ただし、/ tmp/mylogが読み取られるまで、出力はプログラムをブロックします。私はfifoが読み戻さない受信データをフラッシュするのが好きです。どうやってするか?
あなたの質問に触発されて、私はこれを可能にする簡単なプログラムを書きました:
$ myprogram 2>&1 | ftee /tmp/mylog
これはtee
と同様に動作しますが、stdinをstdoutおよび名前付きパイプ(現時点では要件)に複製しますが、ブロックはしません。これは、この方法でログを記録したい場合、ログデータが失われる可能性があることを意味しますが、シナリオでは許容できると思います。トリックはSIGPIPE信号をブロックし、壊れたfifoへの書き込みエラーを無視することです。もちろん、このサンプルはさまざまな方法で最適化されているかもしれませんが、今のところ、私が推測する仕事をしています。
/* ftee - clone stdin to stdout and to a named pipe
(c) racic@stackoverflow
WTFPL Licence */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int readfd, writefd;
struct stat status;
char *fifonam;
char buffer[BUFSIZ];
ssize_t bytes;
signal(SIGPIPE, SIG_IGN);
if(2!=argc)
{
printf("Usage:\n someprog 2>&1 | %s FIFO\n FIFO - path to a"
" named pipe, required argument\n", argv[0]);
exit(EXIT_FAILURE);
}
fifonam = argv[1];
readfd = open(fifonam, O_RDONLY | O_NONBLOCK);
if(-1==readfd)
{
perror("ftee: readfd: open()");
exit(EXIT_FAILURE);
}
if(-1==fstat(readfd, &status))
{
perror("ftee: fstat");
close(readfd);
exit(EXIT_FAILURE);
}
if(!S_ISFIFO(status.st_mode))
{
printf("ftee: %s in not a fifo!\n", fifonam);
close(readfd);
exit(EXIT_FAILURE);
}
writefd = open(fifonam, O_WRONLY | O_NONBLOCK);
if(-1==writefd)
{
perror("ftee: writefd: open()");
close(readfd);
exit(EXIT_FAILURE);
}
close(readfd);
while(1)
{
bytes = read(STDIN_FILENO, buffer, sizeof(buffer));
if (bytes < 0 && errno == EINTR)
continue;
if (bytes <= 0)
break;
bytes = write(STDOUT_FILENO, buffer, bytes);
if(-1==bytes)
perror("ftee: writing to stdout");
bytes = write(writefd, buffer, bytes);
if(-1==bytes);//Ignoring the errors
}
close(writefd);
return(0);
}
次の標準コマンドでコンパイルできます。
$ gcc ftee.c -o ftee
あなたはすぐにそれを確認することができます例えば:
$ ping www.google.com | ftee /tmp/mylog
$ cat /tmp/mylog
また、注意してください-これはマルチプレクサではありません。一度に$ cat /tmp/mylog
を実行できるプロセスは1つだけです。
これは(非常に)古いスレッドですが、最近同様の問題に遭遇しました。実際、私が必要とするのは、非ブロッキングであるパイプへのコピーを含むstdoutへのstdinのクローン作成です。最初の回答で提案されたfteeは本当に役に立ちましたが、(私の使用例では)不安定すぎました。時間内に処理できれば処理できたはずのデータが失われたことを意味します。
私が直面したシナリオは、いくつかのデータを集約し、その結果を3秒ごとにstdoutに書き込むプロセス(some_process)があることです。 (簡略化された)セットアップは次のようになりました(実際のセットアップでは、名前付きパイプを使用しています)。
some_process | ftee >(onlineAnalysis.pl > results) | gzip > raw_data.gz
これで、raw_data.gzを圧縮して完全にする必要があります。 fteeはこの仕事を非常にうまく行っています。しかし、途中で使用しているパイプは遅すぎて、フラッシュされたデータを取得できませんでした。ただし、通常のT字でテストされた場合、到達できる場合はすべてを処理するのに十分な速さでした。ただし、名前のないパイプに何かが発生した場合、通常のTシャツはブロックします。オンデマンドでフックできるようにしたいので、Tシャツはオプションではありません。トピックに戻る:中間にバッファを置くとより良くなり、その結果:
some_process | ftee >(mbuffer -m 32M| onlineAnalysis.pl > results) | gzip > raw_data.gz
しかし、それでも処理できたはずのデータが失われていました。そこで私は先に進んで、以前に提案されたfteeをバッファーバージョン(bftee)に拡張しました。それでもすべて同じプロパティがありますが、書き込みが失敗した場合に備えて(非効率的な?)内部バッファーを使用します。バッファがいっぱいになるとデータは失われますが、私の場合は問題なく動作します。いつものように改善の余地はたくさんありますが、ここからコードをコピーしたので、それを使用する可能性のある人々に共有したいと思います。
/* bftee - clone stdin to stdout and to a buffered, non-blocking pipe
(c) racic@stackoverflow
(c) fabraxias@stackoverflow
WTFPL Licence */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
// the number of sBuffers that are being held at a maximum
#define BUFFER_SIZE 4096
#define BLOCK_SIZE 2048
typedef struct {
char data[BLOCK_SIZE];
int bytes;
} sBuffer;
typedef struct {
sBuffer *data; //array of buffers
int bufferSize; // number of buffer in data
int start; // index of the current start buffer
int end; // index of the current end buffer
int active; // number of active buffer (currently in use)
int maxUse; // maximum number of buffers ever used
int drops; // number of discarded buffer due to overflow
int sWrites; // number of buffer written to stdout
int pWrites; // number of buffers written to pipe
} sQueue;
void InitQueue(sQueue*, int); // initialized the Queue
void PushToQueue(sQueue*, sBuffer*, int); // pushes a buffer into Queue at the end
sBuffer *RetrieveFromQueue(sQueue*); // returns the first entry of the buffer and removes it or NULL is buffer is empty
sBuffer *PeakAtQueue(sQueue*); // returns the first entry of the buffer but does not remove it. Returns NULL on an empty buffer
void ShrinkInQueue(sQueue *queue, int); // shrinks the first entry of the buffer by n-bytes. Buffer is removed if it is empty
void DelFromQueue(sQueue *queue); // removes the first entry of the queue
static void sigUSR1(int); // signal handled for SUGUSR1 - used for stats output to stderr
static void sigINT(int); // signla handler for SIGKILL/SIGTERM - allows for a graceful stop ?
sQueue queue; // Buffer storing the overflow
volatile int quit; // for quiting the main loop
int main(int argc, char *argv[])
{
int readfd, writefd;
struct stat status;
char *fifonam;
sBuffer buffer;
ssize_t bytes;
int bufferSize = BUFFER_SIZE;
signal(SIGPIPE, SIG_IGN);
signal(SIGUSR1, sigUSR1);
signal(SIGTERM, sigINT);
signal(SIGINT, sigINT);
/** Handle commandline args and open the pipe for non blocking writing **/
if(argc < 2 || argc > 3)
{
printf("Usage:\n someprog 2>&1 | %s FIFO [BufferSize]\n"
"FIFO - path to a named pipe, required argument\n"
"BufferSize - temporary Internal buffer size in case write to FIFO fails\n", argv[0]);
exit(EXIT_FAILURE);
}
fifonam = argv[1];
if (argc == 3) {
bufferSize = atoi(argv[2]);
if (bufferSize == 0) bufferSize = BUFFER_SIZE;
}
readfd = open(fifonam, O_RDONLY | O_NONBLOCK);
if(-1==readfd)
{
perror("bftee: readfd: open()");
exit(EXIT_FAILURE);
}
if(-1==fstat(readfd, &status))
{
perror("bftee: fstat");
close(readfd);
exit(EXIT_FAILURE);
}
if(!S_ISFIFO(status.st_mode))
{
printf("bftee: %s in not a fifo!\n", fifonam);
close(readfd);
exit(EXIT_FAILURE);
}
writefd = open(fifonam, O_WRONLY | O_NONBLOCK);
if(-1==writefd)
{
perror("bftee: writefd: open()");
close(readfd);
exit(EXIT_FAILURE);
}
close(readfd);
InitQueue(&queue, bufferSize);
quit = 0;
while(!quit)
{
// read from STDIN
bytes = read(STDIN_FILENO, buffer.data, sizeof(buffer.data));
// if read failed due to interrupt, then retry, otherwise STDIN has closed and we should stop reading
if (bytes < 0 && errno == EINTR) continue;
if (bytes <= 0) break;
// save the number if read bytes in the current buffer to be processed
buffer.bytes = bytes;
// this is a blocking write. As long as buffer is smaller than 4096 Bytes, the write is atomic to a pipe in Linux
// thus, this cannot be interrupted. however, to be save this should handle the error cases of partial or interrupted write none the less.
bytes = write(STDOUT_FILENO, buffer.data, buffer.bytes);
queue.sWrites++;
if(-1==bytes) {
perror("ftee: writing to stdout");
break;
}
sBuffer *tmpBuffer = NULL;
// if the queue is empty (tmpBuffer gets set to NULL) the this does nothing - otherwise it tries to write
// the buffered data to the pipe. This continues until the Buffer is empty or the write fails.
// NOTE: bytes cannot be -1 (that would have failed just before) when the loop is entered.
while ((bytes != -1) && (tmpBuffer = PeakAtQueue(&queue)) != NULL) {
// write the oldest buffer to the pipe
bytes = write(writefd, tmpBuffer->data, tmpBuffer->bytes);
// the written bytes are equal to the buffer size, the write is successful - remove the buffer and continue
if (bytes == tmpBuffer->bytes) {
DelFromQueue(&queue);
queue.pWrites++;
} else if (bytes > 0) {
// on a positive bytes value there was a partial write. we shrink the current buffer
// and handle this as a write failure
ShrinkInQueue(&queue, bytes);
bytes = -1;
}
}
// There are several cases here:
// 1.) The Queue is empty -> bytes is still set from the write to STDOUT. in this case, we try to write the read data directly to the pipe
// 2.) The Queue was not empty but is now -> bytes is set from the last write (which was successful) and is bigger 0. also try to write the data
// 3.) The Queue was not empty and still is not -> there was a write error before (even partial), and bytes is -1. Thus this line is skipped.
if (bytes != -1) bytes = write(writefd, buffer.data, buffer.bytes);
// again, there are several cases what can happen here
// 1.) the write before was successful -> in this case bytes is equal to buffer.bytes and nothing happens
// 2.) the write just before is partial or failed all together - bytes is either -1 or smaller than buffer.bytes -> add the remaining data to the queue
// 3.) the write before did not happen as the buffer flush already had an error. In this case bytes is -1 -> add the remaining data to the queue
if (bytes != buffer.bytes)
PushToQueue(&queue, &buffer, bytes);
else
queue.pWrites++;
}
// once we are done with STDIN, try to flush the buffer to the named pipe
if (queue.active > 0) {
//set output buffer to block - here we wait until we can write everything to the named pipe
// --> this does not seem to work - just in case there is a busy loop that waits for buffer flush aswell.
int saved_flags = fcntl(writefd, F_GETFL);
int new_flags = saved_flags & ~O_NONBLOCK;
int res = fcntl(writefd, F_SETFL, new_flags);
sBuffer *tmpBuffer = NULL;
//TODO: this does not handle partial writes yet
while ((tmpBuffer = PeakAtQueue(&queue)) != NULL) {
int bytes = write(writefd, tmpBuffer->data, tmpBuffer->bytes);
if (bytes != -1) DelFromQueue(&queue);
}
}
close(writefd);
}
/** init a given Queue **/
void InitQueue (sQueue *queue, int bufferSize) {
queue->data = calloc(bufferSize, sizeof(sBuffer));
queue->bufferSize = bufferSize;
queue->start = 0;
queue->end = 0;
queue->active = 0;
queue->maxUse = 0;
queue->drops = 0;
queue->sWrites = 0;
queue->pWrites = 0;
}
/** Push a buffer into the Queue**/
void PushToQueue(sQueue *queue, sBuffer *p, int offset)
{
if (offset < 0) offset = 0; // offset cannot be smaller than 0 - if that is the case, we were given an error code. Set it to 0 instead
if (offset == p->bytes) return; // in this case there are 0 bytes to add to the queue. Nothing to write
// this should never happen - offset cannot be bigger than the buffer itself. Panic action
if (offset > p->bytes) {perror("got more bytes to buffer than we read\n"); exit(EXIT_FAILURE);}
// debug output on a partial write. TODO: remove this line
// if (offset > 0 ) fprintf(stderr, "partial write to buffer\n");
// copy the data from the buffer into the queue and remember its size
memcpy(queue->data[queue->end].data, p->data + offset , p->bytes-offset);
queue->data[queue->end].bytes = p->bytes - offset;
// move the buffer forward
queue->end = (queue->end + 1) % queue->bufferSize;
// there is still space in the buffer
if (queue->active < queue->bufferSize)
{
queue->active++;
if (queue->active > queue->maxUse) queue->maxUse = queue->active;
} else {
// Overwriting the oldest. Move start to next-oldest
queue->start = (queue->start + 1) % queue->bufferSize;
queue->drops++;
}
}
/** return the oldest entry in the Queue and remove it or return NULL in case the Queue is empty **/
sBuffer *RetrieveFromQueue(sQueue *queue)
{
if (!queue->active) { return NULL; }
queue->start = (queue->start + 1) % queue->bufferSize;
queue->active--;
return &(queue->data[queue->start]);
}
/** return the oldest entry in the Queue or NULL if the Queue is empty. Does not remove the entry **/
sBuffer *PeakAtQueue(sQueue *queue)
{
if (!queue->active) { return NULL; }
return &(queue->data[queue->start]);
}
/*** Shrinks the oldest entry i the Queue by bytes. Removes the entry if buffer of the oldest entry runs empty*/
void ShrinkInQueue(sQueue *queue, int bytes) {
// cannot remove negative amount of bytes - this is an error case. Ignore it
if (bytes <= 0) return;
// remove the entry if the offset is equal to the buffer size
if (queue->data[queue->start].bytes == bytes) {
DelFromQueue(queue);
return;
};
// this is a partial delete
if (queue->data[queue->start].bytes > bytes) {
//shift the memory by the offset
memmove(queue->data[queue->start].data, queue->data[queue->start].data + bytes, queue->data[queue->start].bytes - bytes);
queue->data[queue->start].bytes = queue->data[queue->start].bytes - bytes;
return;
}
// panic is the are to remove more than we have the buffer
if (queue->data[queue->start].bytes < bytes) {
perror("we wrote more than we had - this should never happen\n");
exit(EXIT_FAILURE);
return;
}
}
/** delete the oldest entry from the queue. Do nothing if the Queue is empty **/
void DelFromQueue(sQueue *queue)
{
if (queue->active > 0) {
queue->start = (queue->start + 1) % queue->bufferSize;
queue->active--;
}
}
/** Stats output on SIGUSR1 **/
static void sigUSR1(int signo) {
fprintf(stderr, "Buffer use: %i (%i/%i), STDOUT: %i PIPE: %i:%i\n", queue.active, queue.maxUse, queue.bufferSize, queue.sWrites, queue.pWrites, queue.drops);
}
/** handle signal for terminating **/
static void sigINT(int signo) {
quit++;
if (quit > 1) exit(EXIT_FAILURE);
}
このバージョンは、パイプ用にバッファリングされるブロックの数を指定するもう1つの(オプションの)引数を取ります。サンプルコールは次のようになります。
some_process | bftee >(onlineAnalysis.pl > results) 16384 | gzip > raw_data.gz
その結果、破棄が発生する前に16384ブロックがバッファリングされます。これは約32 MBのメモリを消費しますが、...
もちろん、実際の環境では名前付きパイプを使用しているので、必要に応じて着脱できます。次のようになります:
mkfifo named_pipe
some_process | bftee named_pipe 16384 | gzip > raw_data.gz &
cat named_pipe | onlineAnalysis.pl > results
また、プロセスは次のように信号に反応します。SIGUSR1-> STDERR SIGTERMにカウンターを出力し、SIGINT->最初にメインループを終了してバッファをパイプにフラッシュし、2番目はプログラムを即座に終了しました。
多分これは将来の誰かを助ける...楽しむ
ただし、これにより、ドライブの空き容量がなくなるまで使用しなくても、ログファイルは増え続けます。
ログを定期的にローテーションしないのはなぜですか?あなたのためにそれを行うプログラムさえありますlogrotate
。
また、ログメッセージを生成し、タイプに応じてそれらを使用してさまざまな処理を行うシステムもあります。これはsyslog
と呼ばれます。
2つを組み合わせることもできます。プログラムでsyslogメッセージを生成し、syslogを構成してファイルに配置し、logrotateを使用してディスクがいっぱいにならないようにします。
小さな組み込みシステム用に作成していて、プログラムの出力が重いことがわかった場合、さまざまな手法を検討できます。
組み込みデバイスでよく使用されるBusyBoxは、次の方法でRAMバッファ付きログを作成できます。
syslogd -C
で満たすことができます
logger
と読んで
logread
非常にうまく機能しますが、1つのグローバルログしか提供しません。
バッシュのようです<>
リダイレクション演算子( .6.10読み取りおよび書き込み用にファイル記述子を開く )は、それを使用して開かれたファイル/ fifoへの書き込みを非ブロッキングにします。これはうまくいくはずです:
$ mkfifo /tmp/mylog
$ exec 4<>/tmp/mylog
$ myprogram 2>&1 | tee >&4
$ cat /tmp/mylog # on demend
#bash IRCチャネルでgniourf_gniourf)によって与えられる解決策。
組み込みデバイスにscreenをインストールできる場合は、その中で 'myprogram'を実行して切り離し、ログを表示したいときにいつでも再接続できます。何かのようなもの:
$ screen -t sometitle myprogram
Hit Ctrl+A, then d to detach it.
出力を見たいときはいつでも再接続します:
$ screen -DR sometitle
Hit Ctrl-A, then d to detach it again.
これにより、ディスク容量を使用したプログラム出力について心配する必要がなくなります。
プロセスがログファイルに書き込み、その後ファイルをワイプして、時々繰り返し開始するため、ファイルが大きくなりすぎないか、logrotate
を使用します。
tail --follow=name --retry my.log
は、あなたが必要とすることすべてです。端末と同じくらいのスクロールバックが得られます。
非標準は必要ありません。小さなログファイルで試したことはありませんが、すべてのログがこのようにローテーションし、行が失われることに気づいたことはありません。
与えられたfifo
アプローチの問題は、パイプバッファーがいっぱいになり、読み取りプロセスが実行されていないときに、すべてがハングすることです。
fifo
アプローチが機能するためには、 BASH:2つの入力ストリームからの読み取りに最適なアーキテクチャ を参照してください。以下のわずかに変更されたコード、サンプルコード2)。
回避策としては、while ... read
ループ内にカウントメカニズムを実装する名前付きパイプにtee
ing stdoutの代わりにwhile ... read
構文を使用して、指定したログファイルを定期的に上書きすることもできます。行数。これにより、増え続けるログファイル(サンプルコード1)を防ぐことができます。
# sample code 1
# terminal window 1
rm -f /tmp/mylog
touch /tmp/mylog
while sleep 2; do date '+%Y-%m-%d_%H.%M.%S'; done 2>&1 | while IFS="" read -r line; do
lno=$((lno+1))
#echo $lno
array[${lno}]="${line}"
if [[ $lno -eq 10 ]]; then
lno=$((lno+1))
array[${lno}]="-------------"
printf '%s\n' "${array[@]}" > /tmp/mylog
unset lno array
fi
printf '%s\n' "${line}"
done
# terminal window 2
tail -f /tmp/mylog
#------------------------
# sample code 2
# code taken from:
# https://stackoverflow.com/questions/6702474/bash-best-architecture-for-reading-from-two-input-streams
# terminal window 1
# server
(
rm -f /tmp/to /tmp/from
mkfifo /tmp/to /tmp/from
while true; do
while IFS="" read -r -d $'\n' line; do
printf '%s\n' "${line}"
done </tmp/to >/tmp/from &
bgpid=$!
exec 3>/tmp/to
exec 4</tmp/from
trap "kill -TERM $bgpid; exit" 0 1 2 3 13 15
wait "$bgpid"
echo "restarting..."
done
) &
serverpid=$!
#kill -TERM $serverpid
# client
(
exec 3>/tmp/to;
exec 4</tmp/from;
while IFS="" read -r -d $'\n' <&4 line; do
if [[ "${line:0:1}" == $'\177' ]]; then
printf 'line from stdin: %s\n' "${line:1}" > /dev/null
else
printf 'line from fifo: %s\n' "$line" > /dev/null
fi
done &
trap "kill -TERM $"'!; exit' 1 2 3 13 15
while IFS="" read -r -d $'\n' line; do
# can we make it atomic?
# sleep 0.5
# dd if=/tmp/to iflag=nonblock of=/dev/null # flush fifo
printf '\177%s\n' "${line}"
done >&3
) &
# kill -TERM $!
# terminal window 2
# tests
echo hello > /tmp/to
yes 1 | nl > /tmp/to
yes 1 | nl | tee /tmp/to
while sleep 2; do date '+%Y-%m-%d_%H.%M.%S'; done 2>&1 | tee -a /tmp/to
# terminal window 3
cat /tmp/to | head -n 10
ログはUDPソケットに送信される可能性があります。 UDPはコネクションレスなので、送信プログラムをブロックしません。もちろん、受信機やネットワークが追いつかない場合、ログは失われます。
myprogram 2>&1 | socat - udp-datagram:localhost:3333
次に、ロギングを監視する場合:
socat udp-recv:3333 -
複数のリスナーを同時に接続したり、複数のデバイスにブロードキャストしたりできるなど、他にもいくつかの優れたメリットがあります。
Fabraxiasの足跡をたどるために、racicのコードの小さな変更を共有します。私の使用例の1つでは、STDOUT
への書き込みを抑制する必要があったため、別のパラメーターswallow_stdout
を追加しました。 not0
の場合、STDOUT
への出力はオフになります。
私はC
コーダーではないので、コードを読んでいるときにコメントを追加しました。おそらく他の人に役立つかもしれません。
/* ftee - clone stdin to stdout and to a named pipe
(c) racic@stackoverflow
WTFPL Licence */
// gcc /tmp/ftee.c -o /usr/local/bin/ftee
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int readfd, writefd; // read & write file descriptors
struct stat status; // read file descriptor status
char *fifonam; // name of the pipe
int swallow_stdout; // 0 = write to STDOUT
char buffer[BUFSIZ]; // read/write buffer
ssize_t bytes; // bytes read/written
signal(SIGPIPE, SIG_IGN);
if(3!=argc)
{
printf("Usage:\n someprog 2>&1 | %s [FIFO] [swallow_stdout] \n"
"FIFO - path to a named pipe (created beforehand with mkfifo), required argument\n"
"swallow_stdout - 0 = output to PIPE and STDOUT, 1 = output to PIPE only, required argument\n", argv[0]);
exit(EXIT_FAILURE);
}
fifonam = argv[1];
swallow_stdout = atoi(argv[2]);
readfd = open(fifonam, O_RDONLY | O_NONBLOCK); // open read file descriptor in non-blocking mode
if(-1==readfd) // read descriptor error!
{
perror("ftee: readfd: open()");
exit(EXIT_FAILURE);
}
if(-1==fstat(readfd, &status)) // read descriptor status error! (?)
{
perror("ftee: fstat");
close(readfd);
exit(EXIT_FAILURE);
}
if(!S_ISFIFO(status.st_mode)) // read descriptor is not a FIFO error!
{
printf("ftee: %s in not a fifo!\n", fifonam);
close(readfd);
exit(EXIT_FAILURE);
}
writefd = open(fifonam, O_WRONLY | O_NONBLOCK); // open write file descriptor non-blocking
if(-1==writefd) // write file descriptor error!
{
perror("ftee: writefd: open()");
close(readfd);
exit(EXIT_FAILURE);
}
close(readfd); // reading complete, close read file descriptor
while(1) // infinite loop
{
bytes = read(STDIN_FILENO, buffer, sizeof(buffer)); // read STDIN into buffer
if (bytes < 0 && errno == EINTR)
continue; // skip over errors
if (bytes <= 0)
break; // no more data coming in or uncaught error, let's quit since we can't write anything
if (swallow_stdout == 0)
bytes = write(STDOUT_FILENO, buffer, bytes); // write buffer to STDOUT
if(-1==bytes) // write error!
perror("ftee: writing to stdout");
bytes = write(writefd, buffer, bytes); // write a copy of the buffer to the write file descriptor
if(-1==bytes);// ignore errors
}
close(writefd); // close write file descriptor
return(0); // return exit code 0
}