Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 27 Mar 2004 14:44:42 -0500 (EST)
From:      John Wehle <john@feith.com>
To:        christoph.schnauss@berlin.de
Cc:        multimedia@freebsd.org
Subject:   Re: Hauppauge WinTV PVR 250 for FreeBSD 5.2
Message-ID:  <200403271944.i2RJigT06373@jwlab.FEITH.COM>

next in thread | raw e-mail | index | archive | help
>> I'm using a web based VCR application I wrote in C and PHP to record
>> programs.  It's not whizzy, however it gets the job done.  If you're
>> interested I can send you a copy.
>
> I'm interested, and guess, any others too ;-)

Enclosed.

-- John
-------------------------8<----------------------------8<--------------------
# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	tvrec
#	tvrec/README
#	tvrec/etc
#	tvrec/etc/tvrec.sh
#	tvrec/conf
#	tvrec/conf/config.php
#	tvrec/src
#	tvrec/src/Makefile
#	tvrec/src/tvrecd.c
#	tvrec/data
#	tvrec/htdocs
#	tvrec/htdocs/scheddeli.php
#	tvrec/htdocs/sched.php
#	tvrec/htdocs/schedaddi.php
#
echo c - tvrec
mkdir -p tvrec > /dev/null 2>&1
echo x - tvrec/README
sed 's/^X//' >tvrec/README << 'END-of-tvrec/README'
X1) Verify the web environment.  The VCR application is known
X   to work in an environment which has:
X
X     Apache (www.apache.org)         1.3.29
X     PHP (www.php.net)               4.3.4
X     MM (www.ossp.org/pkg/lib/mm/)   1.3.0
X     Berkley DB (www.sleepycat.com)  4.1.25 patchlevel 1
X
X   where PHP is configured with:
X
X     ./configure  --with-apxs=/usr/local/apache/bin/apxs --enable-dba \
X       --with-mm --with-db4=/usr/local/BerkeleyDB --with-flatfile
X
X2) Install the web components.
X
X     mkdir /home/www/tvrec
X     find conf data htdocs -depth -print | cpio -pvumd /home/www/tvrec
X     chown -R root:www /home/www/tvrec
X     chown -R nobody:www /home/www/tvrec/data
X
X3) Configure the web server by adding:
X
X     Alias /tvrec                    /home/www/tvrec/htdocs/
X     Alias /tv-rec                   /home/www/tvrec/htdocs/sched.php
X
X     <Directory /home/www/tvrec/htdocs>
X        AllowOverride None
X        Options None
X        Order deny,allow
X        Deny from all
X        Allow from xxx.xxx.xxx.xxx/xx
X     </Directory>
X
X   to httpd.conf and restarting the server:
X
X     /usr/local/apache/bin/apachectl graceful
X
X4) Install the daemon.
X
X     cd src
X     make
X     cp tvrecd /usr/local/sbin
X     chmod 700 /usr/local/sbin/tvrecd
X     chown root:wheel /usr/local/sbin/tvrecd
X
X5) Edit the start / stop script as necessary to set
X   the directories for the schedule database and
X   video recordings.
X
X6) Install the start / stop script.
X
X     cd etc
X     cp tvrec.sh /usr/local/etc/rc.d
X     chmod 744 /usr/local/etc/rc.d/tvrec.sh
X     chown root:sys /usr/local/etc/rc.d/tvrec.sh
X
X7) Start the daemon.
X
X     /usr/local/etc/rc.d/tvrec.sh start
X
X8) Goto http:/localhost/tv-rec and enter a program
X   to record.
X
X9) Enjoy!
END-of-tvrec/README
echo c - tvrec/etc
mkdir -p tvrec/etc > /dev/null 2>&1
echo x - tvrec/etc/tvrec.sh
sed 's/^X//' >tvrec/etc/tvrec.sh << 'END-of-tvrec/etc/tvrec.sh'
X#!/bin/sh
X#
X# This file should have uid root, gid sys and chmod 744
X#
X
X
XPATH=$PATH:/usr/local/bin
Xexport PATH
X
Xif [ ! -d /usr/local/bin ]
Xthen                    # /usr not mounted
X        exit
Xfi
X
Xkillproc() {            # kill the named process(es)
X        pid=`/bin/ps -ax |
X             /usr/bin/grep -w $1 |
X             /usr/bin/sed -e 's/^  *//' -e 's/ .*//'`
X        [ "$pid" != "" ] && kill $pid
X}
X
X# Start/stop processes required for TVrecd server
X
Xcase "$1" in
X
X'start')
X   echo "Starting TVrecd"
X   /usr/local/sbin/tvrecd -f /home/www/tvrec/data/schedule.db -o /home/multimedia/video/tv
X   ;;
X'stop')
X   echo "Stopping TVrecd"
X   killproc tvrecd
X   ;;
X'restart')
X   $0 stop
X   $0 start
X   ;;
X*)
X   echo "Usage: /usr/local/etc/rc.d/tvrec.sh { start | stop }"
X   ;;
Xesac
X
Xexit 0
END-of-tvrec/etc/tvrec.sh
echo c - tvrec/conf
mkdir -p tvrec/conf > /dev/null 2>&1
echo x - tvrec/conf/config.php
sed 's/^X//' >tvrec/conf/config.php << 'END-of-tvrec/conf/config.php'
X<?php
X
X define ('TVR_BASE', dirname (dirname (__FILE__)) . '/');
X
X $conf['tvr']['webroot'] = '/tvrec';
X
X $conf['tvr']['db_name'] = 'data/schedule.db';
X $conf['tvr']['db_type'] = 'db4';
X
X?>
END-of-tvrec/conf/config.php
echo c - tvrec/src
mkdir -p tvrec/src > /dev/null 2>&1
echo x - tvrec/src/Makefile
sed 's/^X//' >tvrec/src/Makefile << 'END-of-tvrec/src/Makefile'
XCFLAGS = -g -O2
XCPPFLAGS = -I/usr/local/BerkeleyDB/include -I/usr/local/include
XLIBS = -L/usr/local/BerkeleyDB/lib -R/usr/local/BerkeleyDB/lib -ldb
X
Xall: tvrecd
X
Xtvrecd: tvrecd.c
X	$(CC) $(CFLAGS) $(CPPFLAGS) -o tvrecd tvrecd.c $(LIBS)
X
X
Xclean:
X	rm -f *.o
X
X
Xclobber:
X	rm -f *.o
X	rm -f tvrecd
END-of-tvrec/src/Makefile
echo x - tvrec/src/tvrecd.c
sed 's/^X//' >tvrec/src/tvrecd.c << 'END-of-tvrec/src/tvrecd.c'
X/*
X * Copyright (c) 2003
X *	John Wehle <john@feith.com>.  All rights reserved.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X *    notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X *    notice, this list of conditions and the following disclaimer in the
X *    documentation and/or other materials provided with the distribution.
X * 3. All advertising materials mentioning features or use of this software
X *    must display the following acknowledgement:
X *	This product includes software developed by John Wehle.
X * 4. The name of the author may not be used to endorse or promote products
X *    derived from this software without specific prior written permission.
X *
X * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
X * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
X * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
X * DISCLAIMED.	IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
X * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
X * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
X * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
X * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
X * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
X * POSSIBILITY OF SUCH DAMAGE.
X */
X
X/*
X * Daemon for recording TV shows based on a schedule stored
X * in a Berkeley DB.
X */
X
X#include <ctype.h>
X#include <errno.h>
X#include <fcntl.h>
X#include <math.h>
X#include <signal.h>
X#include <stdarg.h>
X#include <stdio.h>
X#include <stdlib.h>
X#include <string.h>
X#include <sys/param.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/wait.h>
X#include <sys/ioctl.h>
X#include <machine/ioctl_meteor.h>
X#include <machine/ioctl_bt848.h>
X#include <poll.h>
X#include <time.h>
X#include <unistd.h>
X
X#include "db.h"
X
X
X#define CHECK_DATABASE_SECONDS     20
X#define DATABASE                   "schedule.db"
X#define LOGFILE                    "/var/log/tvrecd.log"
X
X
Xstatic const char *MyName = "tvrecd";
X
Xstatic int daimon = 0;
Xstatic volatile int shutdown_server = 0;
Xstatic sigset_t block_signal_set;
X
X
Xenum quality { vcd_qt, svcd_qt, dvd_qt };
Xenum repeat { none_rpt, daily_rpt, weekly_rpt };
X
Xstruct program {
X  unsigned int channel;
X  enum quality quality;
X  time_t start;
X  time_t stop;
X  enum repeat repeat;
X  char *name;
X  struct program *next;
X  };
X
X
Xstatic void
Xcatch_signal ()
X  {
X
X  shutdown_server = 1;
X  }
X
X
Xstatic void
Xdaemonize()
X  {
X
X#ifdef SIGTSTP
X  signal(SIGTSTP, SIG_IGN);
X#endif
X#ifdef SIGTTIN
X  signal(SIGTTIN, SIG_IGN);
X#endif
X#ifdef SIGTTOU
X  signal(SIGTTOU, SIG_IGN);
X#endif
X
X  switch (fork ()) {
X    case 0:
X      break;
X
X    case -1:
X      fprintf (stderr, "%s: daemonize -- fork failed.", MyName);
X      perror (MyName);
X      exit (1);
X    /* NOTREACHED */
X      break;
X
X    default:
X      exit (0);
X    /* NOTREACHED */
X      break;
X    }
X
X  setsid();
X
X  close (0);
X  close (1);
X  close (2);
X
X  (void)open ("/dev/null", O_RDWR);
X  (void)open ("/dev/null", O_RDWR);
X  (void)open (LOGFILE, O_WRONLY | O_APPEND | O_CREAT, 0644);
X
X  daimon = 1;
X  }
X
X
Xstatic void
Xerrmsg (const char *fmt, ...)
X  {
X  char time_stamp[256];
X  struct tm *tmp;
X  time_t now;
X  va_list args;
X
X  if (! daimon) {
X    fprintf (stderr, "%s: ", MyName);
X
X    va_start (args, fmt);
X    vfprintf (stderr, fmt, args);
X    va_end (args);
X
X    if (! strchr (fmt, '\n'))
X      fputc ('\n', stderr);
X
X    fflush (stderr);
X    return;
X    }
X
X  if (flock (fileno (stderr), LOCK_EX) == -1) {
X    fprintf (stderr, "%s: errmsg -- can't lock log.\n", MyName);
X    perror (MyName);
X    return;
X    }
X
X  time (&now);
X
X  if ( !(tmp = localtime (&now)) ) {
X    fprintf (stderr, "%s: errmsg -- localtime failed.\n", MyName);
X    perror (MyName);
X    fflush (stderr);
X    (void)flock (fileno (stderr), LOCK_UN);
X    return;
X    }
X
X  strftime (time_stamp, sizeof (time_stamp), "%b %d %H:%M:%S", tmp);
X  fprintf (stderr, "%s %s[%d]: ", time_stamp, MyName, (int)getpid ());
X
X  va_start (args, fmt);
X  vfprintf (stderr, fmt, args);
X  va_end (args);
X
X  if (! strchr (fmt, '\n'))
X    fputc ('\n', stderr);
X
X  fflush (stderr);
X
X  if (flock (fileno (stderr), LOCK_UN) == -1) {
X    fprintf (stderr, "%s: errmsg -- can't unlock log.\n", MyName);
X    perror (MyName);
X    fflush (stderr);
X    return;
X    }
X  }
X
X
Xstatic struct program *
Xfetch_schedule (const char *database)
X  {
X  DB *dbp;
X  DBC *dbcp;
X  DBT data;
X  DBT key;
X  char lock_file[MAXPATHLEN];
X  char *buf;
X  char *ptr;
X  int fd;
X  int len;
X  int ret;
X  struct program *pp;
X  struct program *next;
X  struct tm tm;
X  struct tm *tmp;
X  time_t now;
X
X  len = snprintf (lock_file, sizeof (lock_file), "%s.lck", database);
X  if (len <= 0 || len >= sizeof (lock_file)) {
X    errmsg ("fetch_schedule -- can't generate lock filename.\n");
X    return NULL;
X    }
X
X  if ((fd = open (lock_file, O_RDONLY)) < 0) {
X    char *errstr = strerror (errno);
X
X    errmsg ("fetch_schedule -- can't open lock file.\n");
X    errmsg (errstr);
X    return NULL;
X    }
X
X  if (flock (fd, LOCK_SH) == -1) {
X    char *errstr = strerror (errno);
X
X    errmsg ("fetch_schedule -- can't lock database.\n");
X    errmsg (errstr);
X    close (fd);
X    return NULL;
X    }
X
X  if ((ret = db_create (&dbp, NULL, 0)) != 0) {
X    errmsg ("db_create: %s\n", db_strerror (ret));
X    close (fd);
X    return NULL;
X    }
X
X  dbp->set_errpfx (dbp, MyName);
X
X  if ((ret = dbp->open (dbp, NULL, database, NULL,
X			DB_BTREE, DB_RDONLY | DB_FCNTL_LOCKING, 0)) != 0) {
X    dbp->err (dbp, ret, "DB->open failed for %s", database);
X    dbp->close (dbp, 0);
X    close (fd);
X    return NULL;
X    }
X
X  if ((ret = dbp->cursor (dbp, NULL, &dbcp, 0)) != 0) {
X    dbp->err (dbp, ret, "DB->cursor failed");
X    dbp->close (dbp, 0);
X    close (fd);
X    return NULL;
X    }
X
X  memset (&key, 0, sizeof (key));
X  memset (&data, 0, sizeof (data));
X
X  buf = NULL;
X
X  pp = NULL;
X  next = NULL;
X
X  while ((ret = dbcp->c_get (dbcp, &key, &data, DB_NEXT)) == 0) {
X    if (! (buf = malloc (data.size + 1)) ) {
X      char *errstr = strerror (errno);
X
X      errmsg ("fetch_schedule -- can't allocate buffer.\n");
X      errmsg (errstr);
X      break;
X      }
X
X    memcpy (buf, (char *)data.data, data.size);
X    buf[data.size] = '\0';
X
X    if (! (pp = (struct program *)calloc (1, sizeof (struct program))) ) {
X      char *errstr = strerror (errno);
X
X      errmsg ("fetch_schedule -- can't allocate program.\n");
X      errmsg (errstr);
X      break;
X      }
X
X    if (! (ptr = strtok (buf, ",")) ) {
X      errmsg ("fetch_schedule -- can't parse channel.\n");
X      break;
X      }
X    pp->channel = atoi (ptr);
X
X    if (! (ptr = strtok (NULL, ",")) ) {
X      errmsg ("fetch_schedule -- can't parse quality.\n");
X      break;
X      }
X    pp->quality = dvd_qt;
X    if (strcasecmp (ptr, "vcd") == 0)
X      pp->quality = vcd_qt;
X    else if (strcasecmp (ptr, "svcd") == 0)
X      pp->quality = svcd_qt;
X
X    time (&now);
X    if (! (tmp = localtime (&now)) ) {
X      errmsg ("fetch_schedule -- localtime failed.\n");
X      break;
X      }
X    tm = *tmp;
X
X    if (! (ptr = strtok (NULL, ",")) || ! strptime (ptr, "%m/%d/%Y", &tm)) {
X      errmsg ("fetch_schedule -- can't parse date.\n");
X      break;
X      }
X
X    if (! (ptr = strtok (NULL, ",")) || ! strptime (ptr, "%H:%M", &tm)) {
X      errmsg ("fetch_schedule -- can't parse start.\n");
X      break;
X      }
X    pp->start = mktime (&tm);
X
X    if (! (ptr = strtok (NULL, ",")) || ! strptime (ptr, "%H:%M", &tm)) {
X      errmsg ("fetch_schedule -- can't parse stop.\n");
X      break;
X      }
X    pp->stop = mktime (&tm);
X
X    if (difftime (pp->stop, pp->start) < 0) {
X      tm.tm_mday += 1;
X      pp->stop = mktime (&tm);
X      }
X
X    if (! (ptr = strtok (NULL, ",")) ) {
X      errmsg ("fetch_schedule -- can't parse repeat.\n");
X      break;
X      }
X    pp->repeat = none_rpt;
X    if (strcasecmp (ptr, "daily") == 0)
X      pp->repeat = daily_rpt;
X    else if (strcasecmp (ptr, "weekly") == 0)
X      pp->repeat = weekly_rpt;
X
X    if (pp->repeat == daily_rpt || pp->repeat == weekly_rpt)
X      while (difftime (pp->stop, now) <= 0) {
X	if (! (tmp = localtime (&pp->start)) ) {
X	  errmsg ("fetch_schedule -- can't adjust start.\n");
X	  break;
X	  }
X	tm = *tmp;
X	tm.tm_mday += (pp->repeat == daily_rpt) ? 1 : 7;
X	pp->start = mktime (&tm);
X
X	if (! (tmp = localtime (&pp->stop)) ) {
X	  errmsg ("fetch_schedule -- can't adjust stop.\n");
X	  break;
X	  }
X	tm = *tmp;
X	tm.tm_mday += (pp->repeat == daily_rpt) ? 1 : 7;
X	pp->stop = mktime (&tm);
X	}
X
X    ptr += strlen (ptr) + 1;
X    while (isspace (*ptr))
X      ptr++;
X    if (strpbrk (ptr, "\\/.:")) {
X      errmsg ("fetch_schedule -- invalid character in name.\n");
X      break;
X      }
X    if ( !(pp->name = strdup (ptr)) ) {
X      char *errstr = strerror (errno);
X
X      errmsg ("fetch_schedule -- can't allocate name.\n");
X      errmsg (errstr);
X      break;
X      }
X
X    pp->next = next;
X    next = pp;
X    pp = NULL;
X
X    free (buf);
X    }
X
X  dbcp->c_close (dbcp);
X
X  if (ret != DB_NOTFOUND) {
X    if (ret != 0)
X      dbp->err (dbp, ret, "DBcursor->get failed");
X    if (buf)
X      free (buf);
X    if (pp)
X      free (pp);
X    for (pp = next; pp; pp = next) {
X      next = pp->next;
X      free (pp->name);
X      free (pp);
X      }
X    dbp->close (dbp, 0);
X    close (fd);
X    return NULL;
X    }
X
X  dbp->close (dbp, 0);
X  close (fd);
X
X  if (! next) {
X    if (! (next = (struct program *)calloc (1, sizeof (struct program))) ) {
X      char *errstr = strerror (errno);
X		  
X      errmsg ("fetch_schedule -- can't allocate program.\n");
X      errmsg (errstr);
X      return NULL;
X      }
X    if ( !(next->name = strdup ("")) ) {
X      char *errstr = strerror (errno);
X		
X      errmsg ("fetch_schedule -- can't allocate name.\n");
X      errmsg (errstr);
X      free (next);
X      return NULL;
X      }
X    }
X
X  return next;
X  }
X
X
Xstatic int
Xopen_video_source (unsigned int video_unit)
X  {
X  char video_file[MAXPATHLEN];
X  int fd;
X  int len;
X
X  len = snprintf (video_file, sizeof (video_file), "/dev/bktr%u", video_unit);
X  if (len <= 0 || len >= sizeof (video_file)) {
X    errmsg ("open_video_source -- can't generate video device filename.\n");
X    return -1;
X    }
X
X  if ((fd = open (video_file, O_RDONLY)) < 0) {
X    char *errstr = strerror (errno);
X
X    errmsg ("open_video_source -- can't open %s for input.\n", video_file);
X    errmsg (errstr);
X    return -1;
X    }
X
X  return fd;
X  }
X
X
Xstatic int
Xrecord_program (struct program *pp, const char *output_directory,
X                unsigned int video_unit)
X  {
X  char buffer[512000];
X  char output_file[MAXPATHLEN];
X  char time_stamp[256];
X  int afc;
X  int capture_command;
X  int done;
X  int ofd;
X  int vfd;
X  int len;
X  int ret;
X  sigset_t original_signal_set;
X  ssize_t nbytes_read;
X  struct bktr_capture_area capture_area;
X  struct pollfd fds;
X  struct tm *tmp;
X  time_t now;
X
X  /*
X   * Open the video capture card.
X   */
X
X  if ((vfd = open_video_source (video_unit)) < 0) {
X    errmsg ("record_program -- open_video_source failed.\n");
X    return -1;
X    }
X
X  /*
X   * Set the channel.
X   *
X   * A failure isn't considered fatal since it may simply be
X   * that no station is at the specified channel.
X   */
X
X  afc = 1;
X
X  if (ioctl (vfd, TVTUNER_SETAFC, &afc) < 0) {
X    errmsg ("record_program -- TVTUNER_SETAFC failed.\n");
X    close (vfd);
X    return -1;
X    }
X
X  if (ioctl (vfd, TVTUNER_SETCHNL, &pp->channel) < 0) {
X    close (vfd);
X    return 0;
X    }
X
X  /*
X   * Set the quality.
X   */
X
X  memset (&capture_area, 0, sizeof (capture_area));
X
X  capture_area.x_offset = 0;
X  capture_area.y_offset = 0;
X
X  switch (pp->quality) {
X    case vcd_qt:
X      capture_area.x_size = 352;
X      capture_area.y_size = 240;
X      break;
X
X    case svcd_qt:
X      capture_area.x_size = 480;
X      capture_area.y_size = 480;
X      break;
X
X    default:
X      capture_area.x_size = 720;
X      capture_area.y_size = 480;
X      break;
X    }
X
X  if (ioctl (vfd, BT848_SCAPAREA, &capture_area) < 0) {
X    errmsg ("record_program -- BT848_SCAPAREA failed.\n");
X    close (vfd);
X    return -1;
X    }
X
X  done = 0;
X
X  while (! done) {
X
X    /*
X     * Generate the output filename.
X     */
X
X    if (! (tmp = localtime (&pp->start)) ) {
X      errmsg ("record_program -- localtime failed.\n");
X      close (vfd);
X      return -1;
X      }
X
X    strftime (time_stamp, sizeof (time_stamp), "%m-%d-%Y %H%M", tmp);
X
X    len = snprintf (output_file, sizeof (output_file),
X		    (strlen (pp->name) ? "%s/%s %u %s.mpg"
X				       : "%s/%s %u.mpg"),
X		    output_directory, time_stamp, pp->channel, pp->name);
X    if (len <= 0 || len >= sizeof (output_file)) {
X      errmsg ("record_program -- can't generate output filename.\n");
X      close (vfd);
X      return -1;
X      }
X
X    /*
X     * Open the output file.
X     */
X
X    if ((ofd = open (output_file, O_WRONLY | O_CREAT | O_EXCL, 0644)) < 0) {
X      char *errstr = strerror (errno);
X
X      if (access (output_file, F_OK) == 0) {
X
X        /*
X         * We've already attempted to record the program.
X         */
X
X	close (vfd);
X        return 0;
X        }
X
X      errmsg ("record_program -- can't open %s for output.\n", output_file);
X      errmsg (errstr);
X      close (vfd);
X      return -1;
X      }
X
X    /*
X     * Record the video.
X     */
X
X    while (! done) {
X      if ((nbytes_read = read (vfd, buffer, sizeof (buffer))) <= 0
X	  || write (ofd, buffer, nbytes_read) != nbytes_read) {
X        char *errstr = strerror (errno);
X
X        errmsg ("record_program -- read or write failed.\n");
X        errmsg (errstr);
X        close (vfd);
X        close (ofd);
X        return -1;
X        }
X
X      /*
X       * Allow signals to be processed for .1 millisecond
X       * so that shutdown_server can be updated.
X       */
X
X      sigprocmask (SIG_UNBLOCK, &block_signal_set, &original_signal_set);
X      usleep (100);
X      sigprocmask (SIG_SETMASK, &original_signal_set, NULL);
X
X      time (&now);
X
X      done = shutdown_server || difftime (pp->stop, now) <= 0;
X      }
X
X    capture_command = METEOR_CAP_STOP_CONT;
X
X    if (ioctl (vfd, METEORCAPTUR, &capture_command) < 0) {
X      errmsg ("record_program -- METEORCAPTUR failed.\n");
X      close (vfd);
X      close (ofd);
X      return -1;
X      }
X
X    fds.fd = vfd;
X    fds.events = POLLIN;
X
X    while (poll (&fds, 1, 0) == 1 && (fds.revents & POLLIN))
X      if ((nbytes_read = read (vfd, buffer, sizeof (buffer))) <= 0
X	  || write (ofd, buffer, nbytes_read) != nbytes_read) {
X        char *errstr = strerror (errno);
X
X        errmsg ("record_program -- read or write failed.\n");
X        errmsg (errstr);
X        close (vfd);
X        close (ofd);
X        return -1;
X        }
X
X    close (ofd);
X    }
X
X  close (vfd);
X
X  return 1;
X  }
X
X
Xstatic void
Xusage ()
X  {
X
X  fprintf (stderr, "usage: %s [-d] [-f database] [-o outputdir]\n", MyName);
X  }
X
X
Xint
Xmain (int argc, char **argv)
X  {
X  const char *db;
X  const char *output_directory;
X  double diff;
X  int c;
X  int debug;
X  unsigned int delta;
X  sigset_t original_signal_set;
X  struct program program;
X  struct program *pp;
X  struct program *next;
X  struct stat statbuf;
X  time_t now;
X
X  db = DATABASE;
X  debug = 0;
X  output_directory = ".";
X
X  while ((c = getopt (argc, argv, "df:o:")) != EOF)
X    switch (c) {
X      case 'd':
X	debug = 1;
X	break;
X
X      case 'f':
X	db = optarg;
X	break;
X
X      case 'o':
X	output_directory = optarg;
X	break;
X
X      default:
X	usage ();
X	exit (1);
X      /* NOTREACHED */
X	break;
X      }
X
X  if (stat (output_directory, &statbuf) < 0
X      || (statbuf.st_mode & S_IFMT) != S_IFDIR) {
X    char *errstr = strerror (errno);
X
X    errmsg ("stat failed for %s\n", output_directory);
X    errmsg ("or the path isn't a directory.\n");
X    errmsg (errstr);
X    exit (1);
X    }
X
X  if (! debug)
X    daemonize ();
X
X  sigemptyset(&block_signal_set);
X  sigaddset(&block_signal_set, SIGHUP);
X  sigaddset(&block_signal_set, SIGINT);
X  sigaddset(&block_signal_set, SIGTERM);
X
X  if (signal (SIGHUP, SIG_IGN) != SIG_IGN)
X    (void)signal (SIGHUP, catch_signal);
X  if (signal (SIGINT, SIG_IGN) != SIG_IGN)
X    (void)signal (SIGINT, catch_signal);
X  (void)signal (SIGTERM, catch_signal);
X
X  sigprocmask (SIG_BLOCK, &block_signal_set, NULL);
X
X  while (! shutdown_server) {
X    if (! (pp = fetch_schedule (db)) ) {
X      errmsg ("fetch_schedule failed.\n");
X      exit (1);
X      }
X
X    delta = CHECK_DATABASE_SECONDS;
X    memset (&program, 0, sizeof (program));
X    time (&now);
X
X    while (pp) {
X      next = pp->next;
X
X      if (difftime (now, pp->start) >= 0 && difftime (now, pp->stop) < 0) {
X	program = *pp;
X	program.name = strdup (pp->name);
X
X	if (pp->name && ! program.name) {
X	  char *errstr = strerror (errno);
X
X	  errmsg ("strdup failed.\n");
X	  errmsg (errstr);
X	  exit (1);
X	  }
X	}
X
X      diff = difftime (pp->start, now);
X      if (diff > 0 && diff < delta)
X	delta = (unsigned int) diff;
X
X      free (pp->name);
X      free (pp);
X      pp = next;
X      }
X
X    if (program.start != program.stop) {
X      switch (record_program (&program, output_directory, 0)) {
X	case 1:
X	  continue;
X	/* NOTREACHED */
X	  break;
X
X	case 0:
X
X	  /*
X	   * We've already attempted to record the program.
X	   */
X
X	  delta = CHECK_DATABASE_SECONDS;
X	  diff = difftime (now, program.stop);
X	  if (diff > 0 && diff < delta)
X            delta = (unsigned int) diff;
X	  break;
X
X	default:
X	  errmsg ("record_program failed.\n");
X          exit (1);
X	/* NOTREACHED */
X	  break;
X        }
X      }
X
X    /*
X     * Allow signals to be processed until the next database check is due.
X     */
X
X    sigprocmask (SIG_UNBLOCK, &block_signal_set, &original_signal_set);
X    while (! shutdown_server && (delta = sleep (delta)) != 0)
X      ;
X    sigprocmask (SIG_SETMASK, &original_signal_set, NULL);
X    }
X
X  exit (0);
X  }
END-of-tvrec/src/tvrecd.c
echo c - tvrec/data
mkdir -p tvrec/data > /dev/null 2>&1
echo c - tvrec/htdocs
mkdir -p tvrec/htdocs > /dev/null 2>&1
echo x - tvrec/htdocs/scheddeli.php
sed 's/^X//' >tvrec/htdocs/scheddeli.php << 'END-of-tvrec/htdocs/scheddeli.php'
X<?php
X  require_once (dirname (dirname (__FILE__)) . '/conf/config.php');
X  $tvr_webroot = $conf['tvr']['webroot'];
X
X  $id = dba_open (TVR_BASE . $conf['tvr']['db_name'], 'cl', $conf['tvr']['db_type']);
X
X  dba_delete ($_GET['key'], $id);
X
X  dba_sync ($id);
X
X  dba_close ($id);
X
X  header ('Location: ' . $tvr_webroot . '/sched.php');
X?>
END-of-tvrec/htdocs/scheddeli.php
echo x - tvrec/htdocs/sched.php
sed 's/^X//' >tvrec/htdocs/sched.php << 'END-of-tvrec/htdocs/sched.php'
X<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html401/loose.dtd">;
X
X<HTML>
X  <HEAD>
X         <META HTTP-EQUIV="Expires" CONTENT="-1">
X         <META HTTP-EQUIV="Pragma" CONTENT="no-cache">
X         <META HTTP-EQUIV="Cache-Control" CONTENT="no-store, no-cache, must-revalidate, post-check=0, pre-check=0">
X         <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
X	 <TITLE>Carmen Webserver -- TV Recording</TITLE> 
X  </HEAD>
X
X  <BODY BGCOLOR="#FFCCCC"> 
X	 <H1 ALIGN="CENTER"><FONT COLOR="#CC0000" FACE="Arial">TV
X		recording</FONT></H2><BR>
X
X <TABLE BORDER="1" CELLPADDING="10">
X  <THEADER>
X    <TR>
X      <TD align="center" colspan="9">Current Program(s)</TD>
X    </TR>
X    <TR>
X      <TD>Program</TD>
X      <TD>Channel<BR />1-125</TD>
X      <TD>Quality</TD>
X      <TD>Date<BR />mm/dd/yyyy</TD>
X      <TD>Start<BR />HH:MM</TD>
X      <TD>Stop<BR />HH:MM</TD>
X      <TD>Repeat</TD>
X      <TD>Name</TD>
X      <TD>Action</TD>
X    </TR>
X  </THEADER>
X  <TBODY>
X<?php
X  require_once (dirname (dirname (__FILE__)) . '/conf/config.php');
X  $tvr_webroot = $conf['tvr']['webroot'];
X
X  $id = dba_open (TVR_BASE . $conf['tvr']['db_name'], 'cl', $conf['tvr']['db_type']);
X
X  for ($key = dba_firstkey ($id); $key != false; $key = dba_nextkey ($id)) {
X    $pieces = explode (',', dba_fetch ($key, $id), 7);
X    if (count ($pieces) != 7)
X      continue;
X
X    $pieces[2] = strftime ('%a ', strtotime ($pieces[2])) . $pieces[2];
X
X    for ($i = 0; $i < 7; $i++)
X      $pieces[$i] = htmlspecialchars ($pieces[$i]);
X
X    if (empty ($pieces[6]))
X      $pieces[6] = '<br />';
X
X    echo <<<END
X      <TR>
X        <TD>$key</TD><TD>$pieces[0]</TD><TD>$pieces[1]</TD>
X        <TD>$pieces[2]</TD><TD>$pieces[3]</TD><TD>$pieces[4]</TD>
X        <TD>$pieces[5]</TD><TD>$pieces[6]</TD>
X        <TD><A HREF="$tvr_webroot/sched.php?key=$key">Change
X            <A HREF="$tvr_webroot/scheddeli.php?key=$key">Delete</TD>
X      </TR>
X
XEND;
X    }
X
X  dba_close ($id);
X?>
X
X  </TBODY>
X </TABLE>
X
X <BR>
X
X <TABLE BORDER="1" CELLPADDING="10">
X  <THEADER>
X    <TR>
X      <TD align="center" colspan="9">
X      <?php if (isset ($_GET['key'])
X                || (isset ($_POST['key']) && is_numeric ($_POST['key'])))
X              echo 'Change';
X            else
X              echo 'Add';
X      ?> Program</TD>
X    </TR>
X    <TR>
X      <TD>Program</TD>
X      <TD>Channel<BR />1-125</TD>
X      <TD>Quality</TD>
X      <TD>Date<BR />mm/dd/yyyy</TD>
X      <TD>Start<BR />HH:MM</TD>
X      <TD>Stop<BR />HH:MM</TD>
X      <TD>Repeat</TD>
X      <TD>Name</TD>
X      <TD>Action</TD>
X    </TR>
X  </THEADER>
X
X<?php
X  $key = '-';
X  $pieces = '';
X  if (isset ($_GET['key'])) {
X    $key = $_GET['key'];
X    $id = dba_open (TVR_BASE . $conf['tvr']['db_name'], 'cl', $conf['tvr']['db_type']);
X    $pieces = explode (',', dba_fetch ($key, $id), 7);
X    dba_close ($id);
X    }
X  else if (isset ($_POST['key'])) {
X    $key = $_POST['key'];
X    $pieces = array ($_POST['channel'], $_POST['quality'],
X                     $_POST['date'], $_POST['start'], $_POST['stop'],
X                     $_POST['repeat'], $_POST['name']);
X    }
X
X  if (! is_array ($pieces) || count ($pieces) != 7) {
X    $key = '-';
X    $pieces = array ('', 'DVD', '', '', '', 'None', '');
X    }
X  
X  $svcd_selected = $pieces[1] == 'SVCD' ? 'selected' : '';
X  $vcd_selected = $pieces[1] == 'VCD' ? 'selected' : '';
X
X  $daily_selected = $pieces[5] == 'Daily' ? 'selected' : '';
X  $weekly_selected = $pieces[5] == 'Weekly' ? 'selected' : '';
X
X  for ($i = 0; $i < 7; $i++)
X     $pieces[$i] = htmlspecialchars ($pieces[$i]);
X
X  echo <<<END
X    <form action="$tvr_webroot/schedaddi.php" method="POST">
X    <TBODY>
X      <TR>
X        <TD><select name="key" value="$key"><option>$key</option></select></TD>
X        <TD><input type="text" name="channel" size="3" maxlength="3" value="$pieces[0]"/></TD>
X        <TD><select name="quality">
X              <option>DVD</option>
X              <option $svcd_selected>SVCD</option>
X              <option $vcd_selected>VCD</option>
X            </select></TD>
X        <TD><input type="text" name="date" size="10" maxlength="10" value="$pieces[2]"/></TD>
X        <TD><input type="text" name="start" size="5" maxlength="5" value="$pieces[3]"/></TD>
X        <TD><input type="text" name="stop" size="5" maxlength="5" value="$pieces[4]"/></TD>
X        <TD><select name="repeat">
X              <option>None</option>
X              <option $daily_selected>Daily</option>
X              <option $weekly_selected>Weekly</option>
X            </select></TD>
X        <TD><input type="text" name="name" size="32" maxlength="32" value="$pieces[6]"/></TD>
X        <TD><input type="submit" value="submit" /></TD>
X      </TR>
X    </TBODY>
X    </form>
X
XEND;
X?>
X
X </TABLE>
X
X<?php
X  if (isset ($_POST['key'])) {
X    echo '<BR />';
X    if (isset ($error['key']))
X      echo 'Program must be between 1 and 99.<BR />';
X    if (isset ($error['channel']))
X      echo 'Channel must be between 1 and 125.<BR />';
X    if (isset ($error['date']))
X      echo 'Date should be month/day/year.<BR />';
X    if (isset ($error['start']))
X      echo 'Start time must be hour:minute.<BR />';
X    if (isset ($error['stop']))
X      echo 'Stop time must be hour:minute.<BR />';
X    if (isset ($error['name']))
X      echo 'Name can not contain a backslash, colon, decimal point, or slash.<BR />';
X    }
X?>
X	 <HR> 
X	 <H4 ALIGN="CENTER"><FONT COLOR="#6600FF" FACE="Arial"><A
X		HREF="/"><I>HOME</I></A></FONT></H4><BR>
X</BODY>
X
X</HTML>
END-of-tvrec/htdocs/sched.php
echo x - tvrec/htdocs/schedaddi.php
sed 's/^X//' >tvrec/htdocs/schedaddi.php << 'END-of-tvrec/htdocs/schedaddi.php'
X<?php
X  require_once (dirname (dirname (__FILE__)) . '/conf/config.php');
X  $tvr_webroot = $conf['tvr']['webroot'];
X
X  $error = '';
X
X  if (! is_numeric ($_POST['channel'])
X      || intval ($_POST['channel']) != (double)$_POST['channel']
X      || (int)$_POST['channel'] < 1 || (int)$_POST['channel'] > 125)
X    $error['channel'] = 1;
X
X  $now = getdate ();
X
X  $pieces = explode ('/', $_POST['date']);
X
X  if (count ($pieces) == 1)
X    if (is_numeric ($pieces[0]))
X      $pieces = array ($now['mon'], $pieces[0], $now['year']);
X    else if (! empty ($pieces[0]))
X      $pieces = explode ('/', strftime ('%m/%d/%Y', strtotime ($pieces[0])));
X
X  if (count ($pieces) == 2)
X    $pieces = array ($pieces[0], $pieces[1], $now['year']);
X
X  if (count ($pieces) != 3
X      || ! is_numeric ($pieces[0])
X      || intval ($pieces[0]) != (double)$pieces[0]
X      || ! is_numeric ($pieces[1])
X      || intval ($pieces[1]) != (double)$pieces[1]
X      || ! is_numeric ($pieces[2])
X      || intval ($pieces[2]) != (double)$pieces[2])
X    $error['date'] = 1;
X  else {
X    if ((int)$pieces[2] >= 0 && (int)$pieces[2] < 100) {
X      $century = intval ($now['year'] / 100) * 100;
X      $pieces[2] += $century;
X      }
X
X    if ((int)$pieces[2] < 1000
X        || ! checkdate ($pieces[0], $pieces[1], $pieces[2]) )
X      $error['date'] = 1;
X    else
X      $_POST['date'] = sprintf ('%02d/%02d/%04d',
X                                (int)$pieces[0], (int)$pieces[1],
X                                (int)$pieces[2]);
X     }
X
X  $pieces = explode (':', $_POST['start']);
X
X  if (count ($pieces) != 2
X      || ! is_numeric ($pieces[0])
X      || intval ($pieces[0]) != (double)$pieces[0]
X      || (int)$pieces[0] < 0 || (int)$pieces[0] > 23
X      || ! is_numeric ($pieces[1])
X      || intval ($pieces[1]) != (double)$pieces[1]
X      || (int)$pieces[1] < 0 || (int)$pieces[1] > 59)
X    $error['start'] = 1;
X  else
X    $_POST['start'] = sprintf ('%02d:%02d', (int)$pieces[0], (int)$pieces[1]);
X
X  $pieces = explode (':', $_POST['stop']);
X
X  if (count ($pieces) != 2
X      || ! is_numeric ($pieces[0])
X      || intval ($pieces[0]) != (double)$pieces[0]
X      || (int)$pieces[0] < 0 || (int)$pieces[0] > 23
X      || ! is_numeric ($pieces[1])
X      || intval ($pieces[1]) != (double)$pieces[1]
X      || (int)$pieces[1] < 0 || (int)$pieces[1] > 59)
X    $error['stop'] = 1;
X  else
X    $_POST['stop'] = sprintf ('%02d:%02d', (int)$pieces[0], (int)$pieces[1]);
X
X  if (strpos ($_POST['name'], '\\') !== FALSE
X      || strpos ($_POST['name'], '/') !== FALSE
X      || strpos ($_POST['name'], '.') !== FALSE
X      || strpos ($_POST['name'], ':') !== FALSE)
X    $error['name'] = 1;
X  else
X    $_POST['name'] = trim ($_POST['name']);
X
X  if (is_array ($error)) {
X    include (TVR_BASE . 'htdocs/sched.php');
X    return;
X    }
X
X  $record = $_POST['channel'] . ',' . $_POST['quality'] . ','
X            . $_POST['date'] . ',' . $_POST['start'] . ','
X            . $_POST['stop'] . ',' . $_POST['repeat'] . ','
X            . $_POST['name'];
X
X  $id = dba_open (TVR_BASE . $conf['tvr']['db_name'], 'cl', $conf['tvr']['db_type']);
X
X  $key = '';
X
X  if (isset ($_POST['key']) && is_numeric ($_POST['key']))
X    $key = $_POST['key'];
X  else {
X    $prevkey = 0;
X
X    for ($key = dba_firstkey ($id); $key != false; $key = dba_nextkey ($id)) {
X      if (! is_numeric ($key) )
X        continue;
X      if ((int)$key > (int)($prevkey + 1))
X        break;
X      $prevkey = $key;
X      }
X
X    $key = sprintf ("%02d", $prevkey + 1);
X    }
X
X  if (intval ($key) != (double)$key
X      || (int)$key < 1 || (int)$key > 99) {
X    $error['key'] = 1;
X    dba_close ($id);
X    include (TVR_BASE . 'htdocs/sched.php');
X    return;
X    }
X
X  dba_replace ($key, $record, $id);
X
X  dba_sync ($id);
X
X  dba_close ($id);
X
X  header ('Location: ' . $tvr_webroot . '/sched.php');
X?>
END-of-tvrec/htdocs/schedaddi.php
exit

-------------------------------------------------------------------------
|   Feith Systems  |   Voice: 1-215-646-8000  |  Email: john@feith.com  |
|    John Wehle    |     Fax: 1-215-540-5495  |                         |
-------------------------------------------------------------------------



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200403271944.i2RJigT06373>