From 5d8ab60e14ba7846a49ec6735eeab74f9e0fd364 Mon Sep 17 00:00:00 2001
From: John Sauter <John_Sauter@systemeyescomputerstore.com>
Date: Fri, 8 Jan 2010 16:24:33 -0500
Subject: [PATCH - aplay 2/3] aplay -- add features for audio surveilance

Add signal SIGUSR1 to turn over the output file,
--max-file-time to cause the output file to turn over automatically,
and --use-strftime to create output files based on the current time.

Signed-off-by: John Sauter <John_Sauter@systemeyescomputerstore.com>
---
 aplay/aplay.c |  189 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 181 insertions(+), 8 deletions(-)

diff --git a/aplay/aplay.c b/aplay/aplay.c
index ddb766d..02322f1 100644
--- a/aplay/aplay.c
+++ b/aplay/aplay.c
@@ -111,6 +111,10 @@ static int test_position = 0;
 static int test_coef = 8;
 static int test_nowait = 0;
 static snd_output_t *log;
+static long long max_file_size = 0;
+static int max_file_time = 0;
+static int use_strftime = 0;
+volatile static int recycle_capture_file = 0;
 
 static int fd = -1;
 static off64_t pbrec_count = LLONG_MAX, fdcount;
@@ -199,7 +203,10 @@ _("Usage: %s [OPTION]... [FILE]...\n"
 "    --test-coef=#	 test coeficient for ring buffer position (default 8)\n"
 "                        expression for validation is: coef * (buffer_size / 2)\n"
 "    --test-nowait       do not wait for ring buffer - eats whole CPU\n"
-"    --process-id-file   write the process ID here\n")
+"    --max-file-time=#   start another output file when the old file has recorded\n"
+"                        for this many seconds\n"
+"    --process-id-file   write the process ID here\n"
+"    --use-strftime      apply the strftime facility to the output file name\n")
 		, command);
 	printf(_("Recognized sample formats are:"));
 	for (k = 0; k < SND_PCM_FORMAT_LAST; ++k) {
@@ -363,6 +370,13 @@ static void signal_handler(int sig)
 	exit(EXIT_FAILURE);
 }
 
+/* call on SIGUSR1 signal. */
+static void signal_handler_recycle (int sig)
+{
+	/* flag the capture loop to start a new output file */
+	recycle_capture_file = 1;
+}
+
 enum {
 	OPT_VERSION = 1,
 	OPT_PERIOD_SIZE,
@@ -374,7 +388,9 @@ enum {
 	OPT_TEST_POSITION,
 	OPT_TEST_COEF,
 	OPT_TEST_NOWAIT,
-	OPT_PROCESS_ID_FILE
+	OPT_MAX_FILE_TIME,
+	OPT_PROCESS_ID_FILE,
+	OPT_USE_STRFTIME
 };
 
 int main(int argc, char *argv[])
@@ -415,7 +431,9 @@ int main(int argc, char *argv[])
 		{"test-position", 0, 0, OPT_TEST_POSITION},
 		{"test-coef", 1, 0, OPT_TEST_COEF},
 		{"test-nowait", 0, 0, OPT_TEST_NOWAIT},
+		{"max-file-time", 1, 0, OPT_MAX_FILE_TIME},
 		{"process-id-file", 1, 0, OPT_PROCESS_ID_FILE},
+		{"use-strftime", 0, 0, OPT_USE_STRFTIME},
 		{0, 0, 0, 0}
 	};
 	char *pcm_name = "default";
@@ -606,9 +624,15 @@ int main(int argc, char *argv[])
 		case OPT_TEST_NOWAIT:
 			test_nowait = 1;
 			break;
+		case OPT_MAX_FILE_TIME:
+			max_file_time = strtol(optarg, NULL, 0);
+			break;
 		case OPT_PROCESS_ID_FILE:
 			pidfile_name = optarg;
 			break;
+		case OPT_USE_STRFTIME:
+			use_strftime = 1;
+			break;
 		default:
 			fprintf(stderr, _("Try `%s --help' for more information.\n"), command);
 			return 1;
@@ -681,6 +705,7 @@ int main(int argc, char *argv[])
 	signal(SIGINT, signal_handler);
 	signal(SIGTERM, signal_handler);
 	signal(SIGABRT, signal_handler);
+	signal(SIGUSR1, signal_handler_recycle);
 	if (interleaved) {
 		if (optind > argc - 1) {
 			if (stream == SND_PCM_STREAM_PLAYBACK)
@@ -2425,13 +2450,100 @@ static void playback(char *name)
 		close(fd);
 }
 
+/**
+ * mystrftime
+ *
+ *   Variant of strftime(3) that supports additional format
+ *   specifiers in the format string.
+ *
+ * Parameters:
+ *
+ *   s	  - destination string
+ *   max	- max number of bytes to write
+ *   userformat - format string
+ *   tm	 - time information
+ *   filenumber - the number of the file, starting at 1
+ *
+ * Returns: number of bytes written to the string s
+ */
+size_t mystrftime(char *s, size_t max, const char *userformat,
+		  const struct tm *tm, const int filenumber)
+{
+	char formatstring[PATH_MAX] = "";
+	char tempstring[PATH_MAX] = "";
+	char *format, *tempstr;
+	const char *pos_userformat;
+
+	format = formatstring;
+
+	/* if mystrftime is called with userformat = NULL we return a zero length string */
+	if (userformat == NULL) {
+		*s = '\0';
+		return 0;
+	}
+
+	for (pos_userformat = userformat; *pos_userformat; ++pos_userformat) {
+		if (*pos_userformat == '%') {
+			tempstr = tempstring;
+			tempstr[0] = '\0';
+			switch (*++pos_userformat) {
+
+				case '\0': // end of string
+					--pos_userformat;
+					break;
+
+				case 'v': // file number 
+					sprintf(tempstr, "%02d", filenumber);
+					break;
+
+				default: // All other codes will be handled by strftime
+					*format++ = '%';
+					*format++ = *pos_userformat;
+					continue;
+			}
+
+			/* If a format specifier was found and used, copy the result. */
+			if (tempstr[0]) {
+				while ((*format = *tempstr++) != '\0')
+					++format;
+				continue;
+			}
+		}
+
+		/* For any other character than % we simply copy the character */
+		*format++ = *pos_userformat;
+	}
+
+	*format = '\0';
+	format = formatstring;
+	return strftime(s, max, format, tm);
+}
+
 static int new_capture_file(char *name, char *namebuf, size_t namelen,
 			    int filecount)
 {
-	/* get a copy of the original filename */
 	char *s;
 	char buf[PATH_MAX+1];
+	time_t t;
+	struct tm *tmp;
+
+	if (use_strftime) {
+		t = time(NULL);
+		tmp = localtime(&t);
+		if (tmp == NULL) {
+			perror ("localtime");
+			prg_exit ();
+			exit (EXIT_FAILURE);
+		}
+		if (mystrftime (namebuf, namelen, name, tmp, filecount+1) == 0) {
+			prg_exit ();
+			fprintf (stderr, "mystrftime returned 0");
+			exit (EXIT_FAILURE);
+		}
+		return filecount;
+	}
 
+	/* get a copy of the original filename */
 	strncpy(buf, name, sizeof(buf));
 
 	/* separate extension from filename */
@@ -2463,6 +2575,44 @@ static int new_capture_file(char *name, char *namebuf, size_t namelen,
 	return filecount;
 }
 
+/**
+ * create_path
+ *
+ *   This function creates a file path, like mkdir -p. 
+ *
+ * Parameters:
+ *
+ *   path - the path to create
+ *
+ * Returns: 0 on success, -1 on failure
+ * On failure, a message has been printed to stderr.
+ */
+int create_path(const char *path)
+{
+	char *start;
+	mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
+
+	if (path[0] == '/')
+		start = strchr(path + 1, '/');
+	else
+		start = strchr(path, '/');
+
+	while (start) {
+		char *buffer = strdup(path);
+		buffer[start-path] = 0x00;
+
+		if (mkdir(buffer, mode) == -1 && errno != EEXIST) {
+			fprintf (stderr, "Problem creating directory %s", buffer);
+			perror (" ");
+			free(buffer);
+			return -1;
+		}
+		free(buffer);
+		start = strchr(start + 1, '/');
+	}
+	return 0;
+}
+
 static void capture(char *orig_name)
 {
 	int tostdout=0;		/* boolean which describes output stream */
@@ -2475,6 +2625,8 @@ static void capture(char *orig_name)
 	count = calc_count();
 	if (count == 0)
 		count = LLONG_MAX;
+	/* compute the number of bytes per file */
+	max_file_size = max_file_time * snd_pcm_format_size(hwparams.format, hwparams.rate * hwparams.channels);
 	/* WAVE-file should be even (I'm not sure), but wasting one byte
 	   isn't a problem (this can only be in 8 bit mono) */
 	if (count < LLONG_MAX)
@@ -2501,7 +2653,7 @@ static void capture(char *orig_name)
 		/* open a file to write */
 		if(!tostdout) {
 			/* upon the second file we start the numbering scheme */
-			if (filecount) {
+			if (filecount || use_strftime) {
 				filecount = new_capture_file(orig_name, namebuf,
 							     sizeof(namebuf),
 							     filecount);
@@ -2511,9 +2663,22 @@ static void capture(char *orig_name)
 			/* open a new file */
 			remove(name);
 			if ((fd = open64(name, O_WRONLY | O_CREAT, 0644)) == -1) {
-				perror(name);
-				prg_exit ();
-				exit(EXIT_FAILURE);
+				if ((errno == ENOENT) && use_strftime) {
+					if (create_path (name) == 0) {
+						if ((fd = open64 (name, O_WRONLY | O_CREAT, 0644)) == -1) {
+							perror (name);
+							prg_exit ();
+							exit (EXIT_FAILURE);
+						} 
+					} else {
+						prg_exit ();
+						exit (EXIT_FAILURE);
+					}
+				} else {
+					perror(name);
+					prg_exit ();
+					exit(EXIT_FAILURE);
+				}
 			}
 			filecount++;
 		}
@@ -2521,6 +2686,8 @@ static void capture(char *orig_name)
 		rest = count;
 		if (rest > fmt_rec_table[file_type].max_filesize)
 			rest = fmt_rec_table[file_type].max_filesize;
+		if (max_file_size && (rest > max_file_size)) 
+			rest = max_file_size;
 
 		/* setup sample header */
 		if (fmt_rec_table[file_type].start)
@@ -2528,7 +2695,7 @@ static void capture(char *orig_name)
 
 		/* capture */
 		fdcount = 0;
-		while (rest > 0) {
+		while ((rest > 0) && (recycle_capture_file == 0)) {
 			size_t c = (rest <= (off64_t)chunk_bytes) ?
 				(size_t)rest : chunk_bytes;
 			size_t f = c * 8 / bits_per_frame;
@@ -2544,6 +2711,12 @@ static void capture(char *orig_name)
 			fdcount += c;
 		}
 
+		/* re-enable SIGUSR1 signal */
+		if (recycle_capture_file) {
+			recycle_capture_file = 0;
+			signal(SIGUSR1, signal_handler_recycle);
+		}
+
 		/* finish sample container */
 		if (fmt_rec_table[file_type].end && !tostdout) {
 			fmt_rec_table[file_type].end(fd);
-- 
1.6.3.3

