diff --git a/.github/workflows/cpiped-build.yml b/.github/workflows/cpiped-build.yml
new file mode 100644
index 0000000..c121090
--- /dev/null
+++ b/.github/workflows/cpiped-build.yml
@@ -0,0 +1,21 @@
+name: cpiped build
+
+on:
+ push:
+ branches: [ "master" ]
+ pull_request:
+ branches: [ "master" ]
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: install alsasound
+ run: sudo apt install -y libasound2-dev
+ - uses: actions/checkout@v4
+ - name: make
+ run: make
+ - name: check executable
+ run: bash -c "[[ -x cpiped ]]"
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..dab040f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+cpiped
diff --git a/README.md b/README.md
index 8f8f737..202e382 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,127 @@
-cpiped captures an audio stream and outputs it to a pipe with a bit of buffering
-to handle clock mismatch. It also includes silence/sound detection and the ability
-to run a command on detection.
+
cpiped
-I'm using this to capture line-in audio from a sound card and send it to
-forked-daapd (https://github.com/ejurgensen/forked-daapd). On sound detection, it
-runs a simple script to start playing the associated pipe in forked-daapd.
+[![Release][release-shield]][release-url]
+![GitHub commit activity][commit-shield]
+[![Issues][issues-shield]][issues-url]
+[![Contributors][contributors-shield]][contributors-url]
+[![project_license][license-shield]][license-url]
+[![Stargazers][stars-shield]][stars-url]
+[![Forks][forks-shield]][forks-url]
+![Build flow][build-shield]
+
+![c][c-shield]
+
+ cpiped captures an audio stream and outputs it to a pipe
+
+`cpiped` was born to capture line-in audio from a sound card and send it to [forked-daapd](https://github.com/ejurgensen/forked-daapd). On sound detection, it runs a simple script to start playing the associated pipe in forked-daapd.
+
+## Contents
+- [Contents](#contents)
+- [Features](#features)
+- [Getting Started](#getting-started)
+ - [Prerequisites](#prerequisites)
+ - [Installation](#installation)
+- [Usage](#usage)
+ - [Options](#options)
+ - [Env variables](#env-variables)
+- [ToDo(s)](#todos)
+- [Acknowledgments](#acknowledgments)
+ - [Top contributors:](#top-contributors)
+
+## Features
+- Buffering to handle clock mismatch
+- Command execution on sound and silence detection
+- User defined noise threshold
+- Different sample rate handling
+- Multiple instances running in parallel thanks to parametrized pid
+
+## Getting Started
+
+### Prerequisites
+`cpiped` relies on [ALSA lib](http://www.alsa-project.org) as only prerequisite
+
+- Debian
+ ```sh
+ sudo apt install -y libasound2-dev
+ ```
+
+### Installation
+1. Clone the repository
+ ```sh
+ git clone https://github.com/ale275/cpiped.git
+ ```
+2. Make cpiped
+ ```sh
+ cd cpiped
+ make
+ ```
+4. Install cpiped
+ ```sh
+ sudo install cpiped /usr/local/sbin/
+ ```
+
+## Usage
+
+`cpiped` is an application meant to be run from command line or demonized
+
+```sh
+cpiped [-s arg] [-e arg] [OPTIONS] FIFO
+```
+FIFO: path to the named pipe where sound will be written
+
+### Options
+
+| Option | Description | Type | Default | Range | Required? |
+| ------ | ------------------------------------------------------------- | --------- | ----------------------- | ---------------- | --------- |
+| `-d` | ALSA capture device in *hw:\,\* format. | `string` | `'default'` | | No |
+| `-f` | ALSA capture device sample rate in Hz. | `integer` | `44100` | `16000 - 192000` | No |
+| `-b` | Buffer size in seconds. | `float` | `.25` | `.1 - 5.0` | No |
+| `-s` | Command to run, in background, when sound is detected. | `string` | | | Yes |
+| `-e` | Command to run, in background, when silence is detected. | `string` | | | Yes |
+| `-t` | Silence threshold expressed as signal power. | `integer` | `100` | `1 - 32767` | No |
+| `-D` | Daemonize. | `bool` | | | No |
+| `-p` | Path to pidfile. | `string` | `'/var/run/cpiped.pid'` | | No |
+| `-v` | Log verbosity. (`-vv` and `-vvv` for higher verbosity levels) | `bool` | | | No |
+
+### Env variables
+On start `cpiped` will set the following env variables
+- *CPIPED_SR* audio capture sample rate in *Hz*
+- *CPIPED_SS* audio capture sample size in *bit(s)*
+- *CPIPED_CC* audio capture channel(s) count
+
+Those variables are visible by the sound and silence detect commands
+
+## ToDo(s)
+- [ ] Improve Makefile to create dedicated build folder
+- [ ] Improve Makefile to add install directive
+
+See the [open issues](https://github.com/ale275/cpiped/issues) for a full list of proposed features (and known issues).
+
+## Acknowledgments
+
+* [cpiped](https://github.com/b-fitzpatrick/cpiped) original code of cpiped by [b-fitzpatrick](https://github.com/b-fitzpatrick)
+* [NDpiped](https://github.com/natedreger/NDpiped) modified version writing the buffer only when sound was detected
+* [maweki cpiped fork](https://github.com/maweki/cpiped) for parametrized pid file
+
+### Top contributors:
+
+
+
+
+
+
+[build-shield]: https://github.com/ale275/cpiped/actions/workflows/cpiped-build.yml/badge.svg
+[commit-shield]: https://img.shields.io/github/commit-activity/t/ale275/cpiped?style=flat
+[release-shield]: https://img.shields.io/github/release/ale275/cpiped.svg?colorB=58839b
+[release-url]: https://github.com/ale275/cpiped/releases/latest
+[contributors-shield]: https://img.shields.io/github/contributors/ale275/cpiped.svg
+[contributors-url]: https://github.com/ale275/cpiped/graphs/contributors
+[forks-shield]: https://img.shields.io/github/forks/ale275/cpiped.svg?style=flat
+[forks-url]: https://github.com/ale275/cpiped/network/members
+[stars-shield]: https://img.shields.io/github/stars/ale275/cpiped.svg?style=flat
+[stars-url]: https://github.com/ale275/cpiped/stargazers
+[issues-shield]: https://img.shields.io/github/issues/ale275/cpiped.svg
+[issues-url]: https://github.com/ale275/cpiped/issues
+[license-shield]: https://img.shields.io/github/license/ale275/cpiped.svg
+[license-url]: https://github.com/ale275/cpiped/blob/master/LICENSE
+[c-shield]: https://img.shields.io/badge/made%20with%20C-1f425f?style=flat&logo=c&logoColor=white
\ No newline at end of file
diff --git a/cpiped.c b/cpiped.c
index 1899b34..a4d44f7 100644
--- a/cpiped.c
+++ b/cpiped.c
@@ -20,33 +20,51 @@
// Use the newer ALSA API
#define ALSA_PCM_NEW_HW_PARAMS_API
-#include
-#include
#include
+#include
+#include
#include
#include
+#include
+#include
#include
#include
#include
-#include
-#include
snd_pcm_t *handle;
int daemonize = 0;
+int log_verb = 0;
int readfd = 0;
int writefd = 0;
void mylog(int level, const char *format, ...)
{
- va_list args;
- va_start (args, format);
+ va_list args;
+ va_start (args, format);
+
+ if (daemonize) {
+ vsyslog(level, format, args);
+ } else {
+ vprintf(format, args);
+ }
+
+ va_end(args);
+}
+
+void mylogverb(int level, int msg_verb, const char *format, ...)
+{
+ va_list args_verb;
+ va_start (args_verb, format);
+ if (msg_verb <= log_verb) {
if (daemonize) {
- vsyslog(level, format, args);
+ vsyslog(level, format, args_verb);
} else {
- vprintf(format, args);
+ vprintf(format, args_verb);
}
- va_end(args);
+ }
+
+ va_end(args_verb);
}
void myterm() {
@@ -58,23 +76,55 @@ void myterm() {
exit(EXIT_SUCCESS);
}
+int setenvvar(const char *var_name, size_t var_val_len, unsigned int var_overwrite, const char *format, ...) {
+ va_list args;
+ va_start (args, format);
+
+ char *var_value;
+ int ret;
+
+ // allocate space for the variable value
+ var_value = malloc((var_val_len + 1) * sizeof(char));
+
+ if (var_value == NULL) {
+ mylog(LOG_ERR, "Unable to allocate memory for ENV variable setup '%s'\n", strerror(errno));
+ return 1;
+ }
+
+ // set variable value as string
+ vsnprintf(var_value, var_val_len + 1, format, args);
+
+ // Set the variable
+ ret = setenv(var_name, var_value, var_overwrite);
+
+ // Clean-up
+ free(var_value);
+ va_end(args);
+
+ return ret;
+}
+
int main(int argc, char *argv[]) {
int rc;
int capsize;
snd_pcm_hw_params_t *params;
- unsigned int val = 44100;
+ unsigned int samplerate = 44100;
+ unsigned int samplesize = 16;
+ unsigned int capchannels = 2;
unsigned int capusec;
int dir;
snd_pcm_uframes_t frames;
char *capbuffer;
int16_t *scapbuffer;
char *capdev = "default";
+ char *pidpath = "/var/run/cpiped.pid";
char *fifonam;
struct stat status;
int readbytes = 0;
int writebytes = 0;
char *buf;
- size_t bufsize = 176400;
+ size_t bufsize = 0;
+ float bufdur = 0.25;
int bufstart;
int bufend;
int bufused;
@@ -93,15 +143,14 @@ int main(int argc, char *argv[]) {
char endcmd[1000] = "";
int wrote = 0;
int silentt = 100;
-
+
extern char *optarg;
extern int optind;
int opt;
int err = 0;
-
+
pid_t pid, sid;
char pidstr[10];
- char pidfile[50];
int pidfd;
// Ignore pipe signals
@@ -111,14 +160,14 @@ int main(int argc, char *argv[]) {
signal(SIGTERM, myterm);
// Process command-line options and arguments
- while ((opt = getopt(argc, argv, "d:b:s:e:t:D")) != -1) {
+ while ((opt = getopt(argc, argv, "d:b:s:e:f:t:Dp:v:")) != -1) {
switch (opt) {
case 'd':
capdev = optarg;
break;
case 'b':
- bufsize = atof(optarg) * val * 8;
- if (bufsize < 33280 || bufsize > 1764000) {
+ bufdur = atof(optarg);
+ if (bufdur < 0.1 || bufdur > 5.0) {
mylog(LOG_ERR, "Invalid buffer. Range is 0.1-5.0 sec.\n");
goto error;
}
@@ -146,9 +195,28 @@ int main(int argc, char *argv[]) {
goto error;
}
break;
+ case 'f':
+ samplerate = atoi(optarg);
+ if (samplerate == 0) {
+ mylog(LOG_INFO, "No sample rate specified. Reverting to default.\n");
+ samplerate = 44100;
+ } else if ((samplerate < 16000) || (samplerate > 192000)) {
+ mylog(LOG_ERR, "Invalid sample rate. Range is 16000-192000.\n");
+ goto error;
+ }
+ break;
case 'D':
daemonize = 1;
break;
+ case 'p':
+ pidpath = calloc(strlen(optarg), sizeof(char));
+ sprintf(pidpath, optarg);
+ break;
+ case 'v':
+ log_verb = 1;
+ if (strcmp(optarg, "v" ) == 0) {log_verb = 2;}
+ if (strcmp(optarg, "vv") == 0) {log_verb = 3;}
+ break;
case '?':
err = 1;
mylog(LOG_ERR, "Invalid option: %s\n", argv[optind - 1]);
@@ -158,21 +226,23 @@ int main(int argc, char *argv[]) {
if ((optind + 1) != argc) err = 1;
if (err) {
if (!daemonize) {
- printf("\nUsage:\n %s [-d arg] [-b arg] [-s arg] "
- "[-e arg] [-t arg] [-D] FIFO\n"
+ printf("\nUsage:\n %s [-s arg] [-e arg] [OPTIONS] FIFO\n"
" -d : ALSA capture device ['default']\n"
- " -b : target buffer in seconds [.5]\n"
+ " -b : target buffer in seconds [.25]\n"
" -s : command to run when sound detected\n"
" -e : command to run when silence detected\n"
" -t : silence threshold (1 - 32767, [100])\n"
+ " -f : sample rate (16000 - 192000, [44100])\n"
" -D : daemonize\n"
+ " -p : path to pidfile [/var/run/cpiped.pid]\n"
+ " -v : enable more verbose logs (-vv and -vvv for higher verbosity levels)\n"
" FIFO : path to a named pipe\n", argv[0]);
}
- goto error;
+ exit(EXIT_FAILURE);
}
-
+
mylog(LOG_INFO, "Starting up.\n");
-
+
fifonam = argv[argc - 1];
// Daemonize
@@ -183,15 +253,15 @@ int main(int argc, char *argv[]) {
} else if (pid > 0) {
exit(EXIT_SUCCESS);
}
-
+
umask(0);
-
+
sid = setsid();
if (sid < 0) {
mylog(LOG_ERR, "Error creating new session: %s\n", strerror(errno));
goto error;
}
-
+
if (chdir("/") != 0) {
mylog(LOG_ERR, "Error on chdir /: %s\n", strerror(errno));
goto error;
@@ -199,12 +269,24 @@ int main(int argc, char *argv[]) {
}
// Write the pidfile
- sprintf(pidfile, "/var/run/cpiped.pid");
sprintf(pidstr, "%d\n", getpid());
- pidfd = open(pidfile, O_RDWR|O_CREAT, 0644);
+ pidfd = open(pidpath, O_RDWR|O_CREAT, 0644);
write(pidfd, pidstr, strlen(pidstr));
close(pidfd);
-
+
+ // Log configurations
+ if (log_verb) {
+ mylogverb(LOG_INFO, 1, "Demonize: '%d'\n", daemonize);
+ mylogverb(LOG_INFO, 1, "Log verbosity level: '%d'\n", log_verb);
+ mylogverb(LOG_INFO, 1, "Pid path: '%s'\n", pidpath);
+ mylogverb(LOG_INFO, 1, "Capture device: '%s'\n", capdev);
+ mylogverb(LOG_INFO, 1, "Sample rate: '%d'\n", samplerate);
+ mylogverb(LOG_INFO, 1, "Buffer duration: '%f'\n", bufdur);
+ mylogverb(LOG_INFO, 1, "Sound detect command: '%s'\n", startcmd);
+ mylogverb(LOG_INFO, 1, "Silence detect command: '%s'\n", endcmd);
+ mylogverb(LOG_INFO, 1, "Silence threshold: '%d'\n", silentt);
+ }
+
// Open the FIFO for read first, so the open for write succeeds.
readfd = open(fifonam, O_RDONLY | O_NONBLOCK);
if (readfd <= 0) {
@@ -225,10 +307,7 @@ int main(int argc, char *argv[]) {
goto error;
}
close(readfd);
-
- // Set the FIFO size to 8192 bytes to minimize latency
- fcntl(writefd, F_SETPIPE_SZ, 8192);
-
+
// Open PCM device for recording (capture).
rc = snd_pcm_open(&handle, capdev, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
if (rc != 0) {
@@ -239,8 +318,11 @@ int main(int argc, char *argv[]) {
snd_pcm_hw_params_any(handle, params);
snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
- snd_pcm_hw_params_set_channels(handle, params, 2);
- snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir);
+ snd_pcm_hw_params_set_channels(handle, params, capchannels);
+ snd_pcm_hw_params_set_rate_near(handle, params, &samplerate, &dir);
+
+ // set buffer size in byte
+ bufsize = bufdur * samplerate * capchannels * (samplesize / 8);
// Set period size to 1024 frames.
frames = 1024;
@@ -255,13 +337,23 @@ int main(int argc, char *argv[]) {
// Determine the elapsed time of one period
snd_pcm_hw_params_get_period_time(params, &capusec, &dir);
-
+
// Create a capture buffer large enough to hold one period
snd_pcm_hw_params_get_period_size(params, &frames, &dir);
- capsize = frames * 4; // 2 bytes/sample, 2 channels
+ capsize = frames * (samplesize / 8) * capchannels; // ( / 8) bytes/sample * channels
capbuffer = calloc(capsize, sizeof(char));
scapbuffer = (int16_t*)capbuffer;
-
+
+ // Set the FIFO size to twice the period size
+ int pipesize = fcntl(writefd, F_SETPIPE_SZ, 2 * capsize);
+ if (pipesize < 0) {
+ mylog(LOG_ERR, "Error setting FIFO size: %s\n", strerror(errno));
+ goto error;
+ }
+ if (pipesize != 2 * capsize) {
+ mylog(LOG_NOTICE, "WARN setting FIFO size to %ld, actual size %ld\n", 2*capsize, pipesize);
+ }
+
// Determine write size
writebytes = capsize - 32 ; // Write a bit less to make sure write controls pace
@@ -270,34 +362,20 @@ int main(int argc, char *argv[]) {
buf = calloc(bufsize, sizeof(char));
bufstart = 0;
bufend = 0;
-
- while (1) {
- // Determine the amount of buffer used
- bufused = bufend - bufstart + (bufend < bufstart) * (bufsize - 1);
- //printf("size:%d, start:%d, end:%d, used:%d, wrote:%d\n", bufsize, bufstart, bufend, bufused, wrote);
-
- if (bufused < capsize) {
- // Buffer is almost empty, don't send to pipe
- mylog(LOG_INFO, "Filling buffer\n");
- fillbuf = 1;
- }
-
- if (bufused > (int)bufsize - 1 - capsize) {
- // Buffer is almost full, don't store captured samples
- mylog(LOG_INFO, "Buffer full\n");
- buffull = 1;
- }
- // Resume both capture storage and write to pipe when the buffer reaches half-full
- if ((fillbuf == 1) && (bufused > (int)bufsize / 2))
- fillbuf = 0;
- if ((buffull == 1) && (bufused < (int)bufsize / 2))
- buffull = 0;
-
+ // Export capture parameters to ENV variables
+ if (setenvvar("CPIPED_SR", 6, 1, "%ld", samplerate) == -1)
+ mylog(LOG_NOTICE, "WARN CPIPED_SR to '%ld' FAILED\n", samplerate);
+ if (setenvvar("CPIPED_SS", 2, 1, "%ld", samplesize) == -1)
+ mylog(LOG_NOTICE, "WARN CPIPED_SS to '%ld' FAILED\n", samplesize);
+ if (setenvvar("CPIPED_CC", 2, 1, "%d", capchannels) == -1)
+ mylog(LOG_NOTICE, "WARN CPIPED_CC to '%d' FAILED\n", capchannels);
+
+ while (1) {
// Capture samples
if (!wrote) // When not writing, wait a bit between captures.
usleep(capusec * .95);
- rc = snd_pcm_readi(handle, capbuffer, frames);
+ rc = snd_pcm_readi(handle, capbuffer, frames); // return number of captured frames
if (rc == -EPIPE) { // EPIPE means overrun
mylog(LOG_NOTICE, "Overrun occurred\n");
snd_pcm_prepare(handle);
@@ -306,16 +384,17 @@ int main(int argc, char *argv[]) {
} else if (rc < 0) {
mylog(LOG_ERR, "Capture error: %s\n", snd_strerror(rc));
}
-
- // Compute RMS power level regularly
+
+ // Check if there is sound and if so compute RMS power level regularly and count sounds and silences
if (capcount > 10 && rc > 0) { // Approx. every quarter second
power = 0;
+ // Each frame holds one sample per channel
for (i = 0; i < rc / 2; i++) {
power += pow(scapbuffer[i], 2) + pow(scapbuffer[i + 1], 2);
}
power = sqrt(power/rc);
capcount = 0;
-
+
if (power > silentt) {
soundcount++;
silentcount = 0;
@@ -323,23 +402,23 @@ int main(int argc, char *argv[]) {
silentcount++;
soundcount = 0;
}
-
+
if (silent && soundcount > 1) {
silent = 0;
- mylog(LOG_INFO, "Sound detected. Last two values: %d, %d\n", prevpower, (int)power);
+ mylogverb(LOG_INFO, 0, "Sound detected. Last two values: %d, %d\n", prevpower, (int)power);
if (strcmp(startcmd, "") != 0) {
- mylog(LOG_INFO, "Running '%s'\n", startcmd);
+ mylogverb(LOG_INFO, 0, "Running '%s'\n", startcmd);
if (system(startcmd) != 0) {
mylog(LOG_ERR, "Error running start command.\n");
}
}
}
-
+
if (!silent && silentcount > 55) {
silent = 1;
- mylog(LOG_INFO, "Silence detected. Last two values: %d, %d\n", prevpower, (int)power);
+ mylogverb(LOG_INFO, 0, "Silence detected. Last two values: %d, %d\n", prevpower, (int)power);
if (strcmp(endcmd, "") != 0) {
- mylog(LOG_INFO, "Running '%s'\n", endcmd);
+ mylogverb(LOG_INFO, 0, "Running '%s'\n", endcmd);
if (system(endcmd) != 0) {
mylog(LOG_ERR, "Error running end command.\n");
}
@@ -348,45 +427,72 @@ int main(int argc, char *argv[]) {
}
prevpower = power;
capcount++;
-
- if (!buffull && rc > 0) {
- // Store samples in buffer
- readbytes = rc * 4;
- bufendtoend = bufsize - bufend;
- if (readbytes <= bufendtoend) {
- // No wrap required
- memcpy(buf + bufend, capbuffer, readbytes);
- } else {
- // Wrap required
- memcpy(buf + bufend, capbuffer, bufendtoend);
- memcpy(buf, capbuffer + bufendtoend, readbytes - bufendtoend);
+ // end of soundcounter
+
+ // Check if there is sound, if so write to the pipe
+ if (soundcount > 0) {
+
+ // Determine the amount of buffer used
+ bufused = bufend - bufstart + (bufend < bufstart) * (bufsize - 1);
+ mylogverb(LOG_INFO, 3, "size:%d, start:%d, end:%d, used:%d, wrote:%d\n", bufsize, bufstart, bufend, bufused, wrote);
+
+ if (bufused < capsize) {
+ // Buffer is almost empty, don't send to pipe
+ mylogverb(LOG_INFO, 1, "Filling buffer\n");
+ fillbuf = 1;
}
- bufend = (bufend + readbytes) % bufsize;
- }
-
- wrote = 0;
- if (!fillbuf) {
- bufstarttoend = bufsize - bufstart;
- if (writebytes <= bufstarttoend) { // No wrap required
- rc = write(writefd, buf + bufstart, writebytes);
- if (rc == writebytes) wrote = 1;
- } else { // Wrap required
- rc = write(writefd, buf + bufstart, bufstarttoend);
- rc = write(writefd, buf, writebytes - bufstarttoend);
- if (rc == writebytes - bufstarttoend) wrote = 1;
+
+ if (bufused > (int)bufsize - 1 - capsize) {
+ // Buffer is almost full, don't store captured samples
+ mylogverb(LOG_INFO, 0, "Buffer full\n");
+ buffull = 1;
}
- if (wrote) {
- bufstart = (bufstart + writebytes) % bufsize;
- } else {
- bufstart = (bufstart + readbytes) % bufsize; // Keep buffer used constant
+
+ // Resume both capture storage and write to pipe when the buffer reaches half-full
+ if ((fillbuf == 1) && (bufused > (int)bufsize / 2))
+ fillbuf = 0;
+ if ((buffull == 1) && (bufused < (int)bufsize / 2))
+ buffull = 0;
+
+ if (!buffull && rc > 0) {
+ // Store samples in buffer
+ readbytes = rc * (samplesize / 8) * capchannels;
+ bufendtoend = bufsize - bufend;
+ if (readbytes <= bufendtoend) {
+ // No wrap required
+ memcpy(buf + bufend, capbuffer, readbytes);
+ } else {
+ // Wrap required
+ memcpy(buf + bufend, capbuffer, bufendtoend);
+ memcpy(buf, capbuffer + bufendtoend, readbytes - bufendtoend);
+ }
+ bufend = (bufend + readbytes) % bufsize;
}
- }
+
+ wrote = 0;
+ if (!fillbuf) {
+ bufstarttoend = bufsize - bufstart;
+ if (writebytes <= bufstarttoend) { // No wrap required
+ rc = write(writefd, buf + bufstart, writebytes);
+ if (rc == writebytes) wrote = 1;
+ } else { // Wrap required
+ rc = write(writefd, buf + bufstart, bufstarttoend);
+ rc = write(writefd, buf, writebytes - bufstarttoend);
+ if (rc == writebytes - bufstarttoend) wrote = 1;
+ }
+ if (wrote) {
+ bufstart = (bufstart + writebytes) % bufsize;
+ } else {
+ bufstart = (bufstart + readbytes) % bufsize; // Keep buffer used constant
+ }
+ }
+ } // end if
}
exit(EXIT_SUCCESS);
-
-error:
- if (readfd > 0) close(readfd);
- if (writefd > 0) close(writefd);
- mylog(LOG_INFO, "Stopping due to error.");
- exit(EXIT_FAILURE);
+
+ error:
+ if (readfd > 0) close(readfd);
+ if (writefd > 0) close(writefd);
+ mylog(LOG_INFO, "Stopping due to error.");
+ exit(EXIT_FAILURE);
}