From 285f7ad7d0aed48cd5cb13a2437e80d26fffc254 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 3 Mar 2004 01:25:46 +0000 Subject: commit initial release git-svn-id: file:///home/lennart/svn/public/pgets/trunk@3 768266df-afd4-0310-94a7-d396c829e022 --- Makefile.am | 40 +++++++ bootstrap.sh | 45 ++++++++ configure.ac | 89 ++++++++++++++++ php/pgets-intro.html | 43 ++++++++ php/pgets.php | 119 +++++++++++++++++++++ php/style.css | 39 +++++++ sql/pgets-access.sql | 38 +++++++ sql/pgets.sql | 28 +++++ src/Makefile.am | 47 ++++++++ src/db.h | 29 +++++ src/lock.c | 168 +++++++++++++++++++++++++++++ src/lock.h | 26 +++++ src/modem.c | 76 +++++++++++++ src/modem.h | 26 +++++ src/pgets.c | 294 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/pgets.h | 35 ++++++ src/postgres.c | 93 ++++++++++++++++ src/sqlite.c | 98 +++++++++++++++++ src/util.c | 102 ++++++++++++++++++ src/util.h | 35 ++++++ 20 files changed, 1470 insertions(+) create mode 100644 Makefile.am create mode 100755 bootstrap.sh create mode 100644 configure.ac create mode 100644 php/pgets-intro.html create mode 100644 php/pgets.php create mode 100644 php/style.css create mode 100644 sql/pgets-access.sql create mode 100644 sql/pgets.sql create mode 100644 src/Makefile.am create mode 100644 src/db.h create mode 100644 src/lock.c create mode 100644 src/lock.h create mode 100644 src/modem.c create mode 100644 src/modem.h create mode 100644 src/pgets.c create mode 100644 src/pgets.h create mode 100644 src/postgres.c create mode 100644 src/sqlite.c create mode 100644 src/util.c create mode 100644 src/util.h diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..386d51d --- /dev/null +++ b/Makefile.am @@ -0,0 +1,40 @@ +# $Id$ + +# This file is part of pgets. +# +# pgets is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# pgets is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with pgets; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +EXTRA_DIST=bootstrap.sh README LICENSE +SUBDIRS=src #conf doc man + +MAINTAINERCLEANFILES = README +noinst_DATA = README + +README: + rm -f README +# $(MAKE) -C doc README + cd $(srcdir) && ln -s doc/README README + +homepage: all dist + test -d $$HOME/homepage/lennart + mkdir -p $$HOME/homepage/lennart/projects/pgets + cp *.tar.gz $$HOME/homepage/lennart/projects/pgets +# cp doc/style.css doc/README.html $$HOME/homepage/lennart/projects/pgets/ + cp $$HOME/homepage/lennart/projects/pgets/README.html $$HOME/homepage/lennart/projects/pgets/index.html + +distcleancheck: + @: + +.PHONY: homepage distcleancheck diff --git a/bootstrap.sh b/bootstrap.sh new file mode 100755 index 0000000..9cc81f4 --- /dev/null +++ b/bootstrap.sh @@ -0,0 +1,45 @@ +#!/bin/bash +# $Id$ + +# This file is part of pgets. +# +# pgets is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# pgets is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with pgets; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +run_versioned() { + local P + type -p "$1-$2" &> /dev/null && P="$1-$2" || local P="$1" + + shift 2 + "$P" "$@" +} + +if [ "x$1" = "xam" ] ; then + set -ex + run_versioned automake 1.7 -a -c + ./config.status +else + set -ex + rm -rf autom4te.cache + rm -f config.cache + + run_versioned aclocal 1.7 + autoheader + run_versioned automake 1.7 -a -c + autoconf -Wall + + ./configure --sysconfdir=/etc "$@" + + make clean +fi diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..8d6e9e3 --- /dev/null +++ b/configure.ac @@ -0,0 +1,89 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +# $Id$ + +# This file is part of pgets. +# +# pgets is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# pgets is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with pgets; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +AC_PREREQ(2.59) +AC_INIT([pgets],[0.1],[mzctrgf (at) 0pointer (dot) de]) +AC_CONFIG_SRCDIR([src/pgets.c]) +AC_CONFIG_HEADERS([config.h]) +AM_INIT_AUTOMAKE([foreign -Wall]) + +AC_SUBST(PACKAGE_URL, [http://0pointer.de/lennart/projects/pgets/]) + +if type -p stow > /dev/null && test -d /usr/local/stow ; then + AC_MSG_NOTICE([*** Found /usr/local/stow: default install prefix set to /usr/local/stow/${PACKAGE_NAME}-${PACKAGE_VERSION} ***]) + ac_default_prefix="/usr/local/stow/${PACKAGE_NAME}-${PACKAGE_VERSION}" +fi + +# Checks for programs. +AC_PROG_CC +AC_PROG_LN_S +AC_PROG_MAKE_SET + +# If using GCC specifiy some additional parameters +if test "x$GCC" = "xyes" ; then + CFLAGS="$CFLAGS -pipe -Wall" +fi + +AC_CHECK_LIB([pq], [PQconnectdb]) +AC_CHECK_LIB([sqlite], [sqlite_open]) + +AM_CONDITIONAL([POSTGRES], [true]) +AM_CONDITIONAL([SQLITE], [true]) + +# Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADERS([fcntl.h inttypes.h limits.h stdlib.h string.h termios.h unistd.h]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_TYPE_MODE_T +AC_TYPE_PID_T +AC_TYPE_SIZE_T +AC_STRUCT_TM +AC_TYPE_UID_T +AC_C_VOLATILE + +# Checks for library functions. +AC_TYPE_SIGNAL +AC_FUNC_STAT +AC_CHECK_FUNCS([localtime_r memset strerror strndup]) + +# LYNX documentation generation +AC_ARG_ENABLE(lynx, + AS_HELP_STRING(--disable-lynx,Turn off lynx usage for documentation generation), +[case "${enableval}" in + yes) lynx=yes ;; + no) lynx=no ;; + *) AC_MSG_ERROR(bad value ${enableval} for --disable-lynx) ;; +esac],[lynx=yes]) + +if test x$lynx = xyes ; then + AC_CHECK_PROG(have_lynx, lynx, yes, no) + + if test x$have_lynx = xno ; then + AC_MSG_ERROR([*** Sorry, you have to install lynx or use --disable-lynx ***]) + fi +fi + +AM_CONDITIONAL([USE_LYNX], [test "x$lynx" = xyes]) + +AC_CONFIG_FILES([src/Makefile Makefile]) # conf/Makefile doc/Makefile doc/README.html]) +AC_OUTPUT diff --git a/php/pgets-intro.html b/php/pgets-intro.html new file mode 100644 index 0000000..98a66c2 --- /dev/null +++ b/php/pgets-intro.html @@ -0,0 +1,43 @@ + + + + + +pgETS + + + + +

ETS Call Accounting - Make Your Selection

+ +
+ +

Direction

+
+
Outgoing + Incoming
+
Outgoing
+
Incoming
+
+ +

Sort Order

+
+
Ascending
+
Descending
+
+ +

Time Range

+

Format is YYYY-MM-DD, leave empty for complete list.

+

Between and

+
+ +
+ +
+

+Shortcuts: Complete List | Outgoing This Month +

+ +
+
Last modified: Mon Mar 1 00:03:59 CET 2004
+ + diff --git a/php/pgets.php b/php/pgets.php new file mode 100644 index 0000000..d1e8036 --- /dev/null +++ b/php/pgets.php @@ -0,0 +1,119 @@ +' ?> + + + + +pgETS + + + + +

ETS Call Accounting - Query Results

+"; + + if ($edate != "" && $sdate != "") { + echo "Showing entries between $sdate (incl) and $edate (excl)."; + } else if ($edate != "") { + echo "Showing entries before $edate (excl)."; + } else if ($sdate != "") { + echo "Showing entries after $sdate (incl)."; + } + + echo "

"; +} + +$db = pg_pconnect($dbspec); + +$where = "WHERE 1=1"; +if ($n_direction == 1) $where = $where." AND incoming='f'"; +if ($n_direction == 2) $where = $where." AND incoming='t'"; +if ($sdate != "") $where = $where." AND _timestamp >= '$sdate'"; +if ($edate != "") $where = $where." AND _timestamp < '$edate'"; + +$q = pg_query($db, "SELECT CASE WHEN SUBSTRING(remote_msn FROM 1 FOR 3)='".$local_prefix."' THEN SUBSTRING(TRIM(remote_msn) FROM 4) ELSE TRIM(remote_msn) END, CASE WHEN incoming='t' THEN 'Incoming' ELSE 'Outgoing' END, participant, TO_CHAR(_timestamp, 'DD.MM.YYYY HH24:MI'), CASE WHEN duration > 60 THEN duration/60||'m '||duration%60||'s' ELSE duration||'s' END FROM pgets_accounting ".$where." ORDER BY _timestamp ".$order); + +$num = pg_numrows($q); + +if ($num == 0) { + echo "

No entries found.

\n"; +} else { + echo "\n\n"; + + $c = 0; + + for ($i = 0; $i < $num; $i++) { + $r = pg_fetch_row($q, $i); + + + $c = 1-$c; + + echo ""; + + for ($j=0; $j < count($r); $j++) { + echo ""; + } + + echo "\n"; + } + + echo "
Remote MSNDirectionP.Start timeDuration
 $r[$j] 
\n"; + + $q = pg_query($db, "SELECT COUNT(*),SUM(duration) FROM pgets_accounting $where"); + $r = pg_fetch_row($q, 0); + + if ($r[1] > 3600) { + $sum = (int) ($r[1]/3600)."h ".(int)($r[1] % 3600 / 60)."m ".(int)($r[1] % 60)."s"; + } else if ($r[1] > 60) { + $sum = (int)($r[1]/60)."m ".(int)($r[1] % 60)."s"; + } else { + $sum = $r[1]."s"; + } + + echo "

$r[0] items, $sum total duration.

"; +} + +?> + +
+
Generated:
+ + \ No newline at end of file diff --git a/php/style.css b/php/style.css new file mode 100644 index 0000000..ae6132c --- /dev/null +++ b/php/style.css @@ -0,0 +1,39 @@ +/* $Id$ */ + +/*** + * This file is part of pgets. + * + * pgets is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * pgets is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with pgets; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + ***/ + +body { color: black; background-color: white; margin: 0.5cm; } +a:link, a:visited { color: #900000; } +p { margin-left: 0.5cm; margin-right: 0.5cm; } +div.news-date { margin-left: 0.5cm; font-size: 80%; color: #4f0000; } +p.news-text { margin-left: 1cm; } +h1 { color: #00009F; } +h2 { color: #00009F; } +h3 { color: #00004F; margin-left: 0.5cm; } +ul { margin-left: .5cm; } +ol { margin-left: .5cm; } +pre { margin-left: .5cm; background-color: #f0f0f0; padding: 0.4cm;} +.grey { color: #afafaf; } + +.radiobox { margin-left: .5cm; background-color:#f0f0f0; padding: 0.4cm; } +.line0 { background-color:#f0f0f0; } +.line1 { background-color:#ffffff; } +.theader { background-color:#d0d0d0; } + +table { border:1px solid black; } diff --git a/sql/pgets-access.sql b/sql/pgets-access.sql new file mode 100644 index 0000000..e4507f5 --- /dev/null +++ b/sql/pgets-access.sql @@ -0,0 +1,38 @@ +-- $Id$ +-- +-- This file is part of pgets. +-- +-- pgets is free software; you can redistribute it and/or modify it under +-- the terms of the GNU General Public License as published by the Free +-- Software Foundation; either version 2 of the License, or (at your +-- option) any later version. +-- +-- pgets is distributed in the hope that it will be useful, but WITHOUT +-- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +-- for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with pgets; if not, write to the Free Software Foundation, +-- Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +-- Create database + +DROP DATABASE pgets; +CREATE DATABASE pgets; + +\c pgets + +-- Create tables + +\i pgets.sql + +-- Manage access rights + +DROP USER pgets_fill; +CREATE USER pgets_fill PASSWORD 'mahatma'; +DROP USER pgets_web; +CREATE USER pgets_web PASSWORD 'gandhi'; + +GRANT INSERT ON pgets_accounting TO pgets_fill; +GRANT SELECT ON pgets_accounting TO pgets_web; diff --git a/sql/pgets.sql b/sql/pgets.sql new file mode 100644 index 0000000..0ba22aa --- /dev/null +++ b/sql/pgets.sql @@ -0,0 +1,28 @@ +-- $Id$ +-- +-- This file is part of pgets. +-- +-- pgets is free software; you can redistribute it and/or modify it under +-- the terms of the GNU General Public License as published by the Free +-- Software Foundation; either version 2 of the License, or (at your +-- option) any later version. +-- +-- pgets is distributed in the hope that it will be useful, but WITHOUT +-- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +-- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +-- for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with pgets; if not, write to the Free Software Foundation, +-- Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +CREATE TABLE pgets_accounting ( + remote_msn CHAR(22) NOT NULL, + local_mm SMALLINT NOT NULL, + participant SMALLINT NOT NULL, + incoming BOOL NOT NULL, + _timestamp TIMESTAMP NOT NULL, + duration INT NOT NULL); + +CREATE INDEX pgets_accounting_timestamp ON pgets_accounting(_timestamp); +CREATE UNIQUE INDEX pgets_accounting_unique ON pgets_accounting(remote_msn, local_mm, participant, incoming, _timestamp, duration); diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..ea26863 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,47 @@ +# $Id$ + +# This file is part of pgets. +# +# pgets is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# pgets is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with pgets; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +sbin_PROGRAMS = + +if SQLITE +sbin_PROGRAMS += pgets-sqlite +endif + +if POSTGRES +sbin_PROGRAMS += pgets-postgres +endif + + +SOURCES = pgets.c pgets.h \ + modem.c modem.h \ + lock.c lock.h \ + util.c util.h \ + db.h + +pgets_sqlite_SOURCES = $(SOURCES) sqlite.c +pgets_sqlite_LDADD = $(LDADD) $(SQLITE_LIBS) +pgets_sqlite_CLFAGS = $(AM_CFLAGS) $(SQLITE_CFLAGS) + +pgets_postgres_SOURCES = $(SOURCES) postgres.c +pgets_postgres_LDADD = $(LDADD) $(POSTGRES_LIBS) +pgets_postgres_CLFAGS = $(AM_CFLAGS) $(POSTGRES_CFLAGS) + +svnkeywords: + svn propset svn:keywords Id *.c *.h Makefile.am + +.PHONY: svnkeywords diff --git a/src/db.h b/src/db.h new file mode 100644 index 0000000..e8033cb --- /dev/null +++ b/src/db.h @@ -0,0 +1,29 @@ +#ifndef foodbhfoo +#define foodbhfoo + +/* $Id$ + * + * This file is part of pgets. + * + * pgets is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * pgets is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with pgets; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "pgets.h" + +void* db_connect(const char*a); +void db_disconnect(void *t); +int db_write_entry(void *t, const struct entry *entry); + +#endif diff --git a/src/lock.c b/src/lock.c new file mode 100644 index 0000000..91878d4 --- /dev/null +++ b/src/lock.c @@ -0,0 +1,168 @@ +/* $Id$ + * + * This file is part of pgets. + * + * pgets is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * pgets is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with pgets; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#define _GNU_SOURCE + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "lock.h" +#include "util.h" + +#ifndef LOCKDIR +#define LOCKDIR "/var/lock" +#endif + +static const char *lockfile(const char *dev) { + static char lockfile[PATH_MAX]; + snprintf(lockfile, sizeof(lockfile), LOCKDIR"/LCK..%s", basename((char*) dev)); + return lockfile; +} + +static const char *tempfile(const char *path) { + static char t[PATH_MAX]; + snprintf(t, sizeof(t), "%s.tmp.%lu", path, (unsigned long) getpid()); + return t; +} + +int device_lock(const char *dev, const char *appname) { + struct stat st; + int fd; + const char *path, *temp; + char buf[100]; + char uidbuf[32]; + uid_t uid; + + if (stat(LOCKDIR, &st) != 0 || !S_ISDIR(st.st_mode)) { + fprintf(stderr, "Failed to lock device, directory "LOCKDIR" not existent.\n"); + return -1; + } + + path = lockfile(dev); + temp = tempfile(path); + + for (;;) { + mode_t u; + struct passwd* pw; + char *username; + + if ((fd = open(path, O_RDONLY)) < 0) { + if (errno != ENOENT) { + fprintf(stderr, "Failed to open lock file: %s\n", strerror(errno)); + return -1; + } + } + + if (fd >= 0) { + ssize_t n; + + n = loop_read(fd, buf, sizeof(buf) - 1); + close(fd); + + if (n < 0) { + close(fd); + fprintf(stderr, "Failed to read from lock file: %s\n", strerror(errno)); + return -1; + } + + if (n > 0) { + pid_t pid; + + if (n == 4) + pid = (pid_t) *((uint32_t*) buf); + else { + unsigned long v; + buf[n] = 0; + sscanf(buf, "%lu", &v); + pid = (pid_t) v; + } + + if (pid > 0) { + if (kill(pid, 0) < 0 && errno == ESRCH) { + fprintf(stderr, "Lockfile is stale. Overriding it.\n"); + /* Yes, here is a race condition */ + unlink(path); + } else + return 1; + } + } + } + + u = umask(0033); + fd = open(temp, O_WRONLY | O_CREAT | O_EXCL, 0666); + umask(u); + + if (fd < 0) { + fprintf(stderr, "Failed to create temporary lock file: %s\n", strerror(errno)); + return -1; + } + + uid = getuid(); + + if ((pw = getpwuid(uid))) + username = pw->pw_name; + else + snprintf(username = uidbuf, sizeof(uidbuf), "%lu", (unsigned long) uid); + + snprintf(buf, sizeof(buf), "%10lu %s %.20s\n", (unsigned long) getpid(), appname, username); + if (loop_write(fd, buf, strlen(buf)) < 0) { + fprintf(stderr, "Failed to write to temporary lock file: %s\n", strerror(errno)); + close(fd); + return -1; + } + + close(fd); + + if (link(temp, path) < 0) { + if (errno == EEXIST) + continue; + + fprintf(stderr, "Failed to link temporary lock file: %s\n", strerror(errno)); + } + + unlink(temp); + + return 0; + } +} + +int device_unlock(const char *dev) { + + if (unlink(lockfile(dev)) < 0) { + fprintf(stderr, "Failed to remove lock file: %s\n", strerror(errno)); + return -1; + } + + return 0; +} diff --git a/src/lock.h b/src/lock.h new file mode 100644 index 0000000..ec1ccd8 --- /dev/null +++ b/src/lock.h @@ -0,0 +1,26 @@ +#ifndef foolockhfoo +#define foolockhfoo + +/* $Id$ + * + * This file is part of pgets. + * + * pgets is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * pgets is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with pgets; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +int device_lock(const char *dev, const char *appname); +int device_unlock(const char *dev); + +#endif diff --git a/src/modem.c b/src/modem.c new file mode 100644 index 0000000..677d4cf --- /dev/null +++ b/src/modem.c @@ -0,0 +1,76 @@ +/* $Id$ + * + * This file is part of pgets. + * + * pgets is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * pgets is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with pgets; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include +#include +#include +#include +#include + +#include "modem.h" + +static struct termios _saved_termios; + +/* Opens the modem and sets the baud rate */ +int modem_open(const char *dev) { + struct termios pts; + int fd, n; + + if ((fd = open(dev, O_RDWR|O_NDELAY)) < 0) { + perror("Serial port open failure"); + return -1; + } + + n = fcntl(fd, F_GETFL, 0); + fcntl(fd, F_SETFL, n & ~O_NDELAY); + + if (tcgetattr(fd, &_saved_termios) != 0) { + perror("Serial port TERMIOS get failure"); + close(fd); + return -1; + } + + memset(&pts, 0, sizeof pts); + pts.c_cflag = CS8 | CLOCAL | CREAD; + pts.c_iflag = IGNPAR | IGNBRK | IGNCR | IXON | IXOFF; + pts.c_oflag = 0; + pts.c_lflag = 0; + + pts.c_cc[VMIN] = 1; + pts.c_cc[VTIME] = 0; + + cfsetospeed(&pts, B9600); + cfsetispeed(&pts, B9600); + + tcflush(fd, TCIFLUSH); + if (tcsetattr(fd, TCSANOW, &pts) != 0) { + perror("Serial port TERMIOS set failure"); + close(fd); + return -1; + } + + return fd; +} + +/* Closes the modem device and resets the baudrate */ +void modem_close(int fd) { + tcflush(fd, TCIFLUSH); + tcsetattr(fd, TCSANOW, &_saved_termios); + close(fd); +} diff --git a/src/modem.h b/src/modem.h new file mode 100644 index 0000000..0cc8c73 --- /dev/null +++ b/src/modem.h @@ -0,0 +1,26 @@ +#ifndef foomodemhfoo +#define foomodemhfoo + +/* $Id$ + * + * This file is part of pgets. + * + * pgets is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * pgets is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with pgets; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +int modem_open(const char *dev); +void modem_close(int fd); + +#endif diff --git a/src/pgets.c b/src/pgets.c new file mode 100644 index 0000000..993111e --- /dev/null +++ b/src/pgets.c @@ -0,0 +1,294 @@ +/* $Id$ + * + * This file is part of pgets. + * + * pgets is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * pgets is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with pgets; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include + +#include "lock.h" +#include "modem.h" +#include "util.h" +#include "db.h" + +#define ETS_RESET ('$') +#define ETS_NEXT ('%') + +int verbose = 1; +volatile int quit = 0; + +void sigint(int s) { + const char *e = "\r*** Got SIGINT, exiting *** \n"; + loop_write(2, e, strlen(e)); + quit = 1; +} + +int ets_reset(int fd) { + char c = ETS_RESET; + return (write(fd, &c, sizeof(c)) != sizeof(c)) ? -1 : 0; +} + +int ets_next(int fd) { + char c = ETS_NEXT; + return (write(fd, &c, sizeof(c)) != sizeof(c)) ? -1 : 0; +} + +int atoi_l(char *s, int l) { + char *p = strndup(s, l); + int r = atoi(p); + free(p); + return r; +} + +int ets_parse(struct entry *entry, char *ln) { + int i, t; + memset(entry, 0, sizeof(struct entry)); + + if (ln[0] != '*') + for (i = 0; i <= 20; i++) { + if (ln[i] >= '0' && ln[i] <= '9') + entry->remote_msn[i] = ln[i]; + else + break; + } + + if (ln[25] >= '0' && ln[25] <= '9' && ln[26] >= '0' && ln[26] <= '9') { + entry->local_mm = atoi_l(ln+25, 2); + + if (entry->local_mm <= 0) { + fprintf(stderr, "Corrupt local MM column.\n"); + return -1; + } + } + + if (ln[32] >= '0' && ln[32] <= '9' && ln[33] >= '0' && ln[33] <= '9') { + entry->participant = atoi_l(ln+32, 2); + + if (!((entry->participant >= 31 && entry->participant <= 38) || + (entry->participant >= 41 && entry->participant <= 48))) { + fprintf(stderr, "Corrupt participant column.\n"); + return -1; + } + } + + entry->incoming = ln[35] == 'K' ? 1 : 0; + + if (!(ln[39] >= '0' && ln[39] <= '9' && ln[40] >= '0' && ln[40] <= '9')) { + fprintf(stderr, "Corrupt day column.\n"); + return -1; + } + + if (!(ln[42] >= '0' && ln[42] <= '9' && ln[43] >= '0' && ln[43] <= '9')) { + fprintf(stderr, "Corrupt month column.\n"); + return -1; + } + + if (!(ln[45] >= '0' && ln[45] <= '9' && ln[46] >= '0' && ln[46] <= '9')) { + fprintf(stderr, "Corrupt hour column.\n"); + return -1; + } + + if (!(ln[48] >= '0' && ln[48] <= '9' && ln[49] >= '0' && ln[49] <= '9')) { + fprintf(stderr, "Corrupt minute column.\n"); + return -1; + } + + entry->day = atoi_l(ln+39, 2); + entry->month = atoi_l(ln+42, 2); + entry->hour = atoi_l(ln+45, 2); + entry->minute = atoi_l(ln+48, 2); + + if (entry->day < 1 || entry->day > 31 || entry->month < 1 || entry->month > 12 || entry->hour < 0 || entry->hour > 24 || entry->minute < 0 || entry->minute >= 60) { + fprintf(stderr, "Corrupt timespec column.\n"); + return -1; + } + + if (!((ln[51] == ' ' || (ln[51] >= '0' && ln[51] <= '9')) && (ln[52] == ' ' || (ln[52] >= '0' && ln[52] <= '9')) && ln[53] >= '0' && ln[53] <= '9')) { + fprintf(stderr, "Corrupt minute (duration) column.\n"); + return -1; + } + + if (!(ln[55] >= '0' && ln[55] <= '9' && ln[56] >= '0' && ln[56] <= '9')) { + fprintf(stderr, "Corrupt second (duration) column.\n"); + return -1; + } + + t = atoi_l(ln+55, 2); + + if (t < 0 || t >= 60) { + fprintf(stderr, "Corrupt second (duration) column. (#2)\n"); + return -1; + } + + entry->duration = atoi_l(ln+51,3)*60 + t; + + if (entry->duration < 0) { + fprintf(stderr, "Corrupt duration column.\n"); + return -1; + } + + return 0; +} + +int ets_read_entry(int fd, struct entry *entry) { + char ln[80]; + int r; + + if ((r = read_line(fd, ln, sizeof(ln))) != 74) { + if (r == 1 && ln[0] == '\n') + return -2; + + fprintf(stderr, "Error while reading line (%i|%i).\n", r, (int) ln[0]); + return -1; + } + + if (ln[73] != '\n') { + fprintf(stderr, "Corrupt line.\n"); + return -1; + } + + ln[73] = 0; + + if (ln[37] == 'V') + return 0; + + if (ets_parse(entry, ln) == 0) + return 1; + + fprintf(stderr, "Failure in line [%s]\n", ln); + return -1; +} + + +void work(int fd, void *db) { + int n = 0, v = 0, a = 0; + if (ets_reset(fd) != 0) { + fprintf(stderr, "Could not reset PBX.\n"); + return; + } + + // Accelerate a bit + if (ets_next(fd) != 0) + return; + + while (!quit) { + struct entry entry; + int r; + + if ((r = ets_read_entry(fd, &entry)) < 0) + break; + + if (r > 0) { + v++; + + if ((r = db_write_entry(db, &entry)) < 0) + break; + else if (r == 0) + a++; + } + + if (verbose) { + fprintf(stderr, "%3.1f%% done; %i of %i valid entries added.\r", (float)(++n)/10, a, v); + fflush(stdout); + } + + if (ets_next(fd) != 0) + break; + } + + if (verbose) + fprintf(stderr, "Finished; %i entries in PBX; %i of %i valid entries added.\n", n, a, v); +} + +void help(const char *p) { + fprintf(stderr, + "%s [-h] [-q] [-d DEVICE] [-b DATABASE]\n" + " -h Shows this help\n" + " -q Disables verbose mode\n" + " -d DEVICE specifies the serial device to use\n" + " -b DATABASE specifies the database to use\n", + p); + exit(1); +} + +int main(int argc, char *argv[]) { + int fd = -1, r = 1, locked = 0; + const char *dev = "/dev/ttyS1"; + void *db = NULL; + char* dbspec = NULL; + int c; + + while ((c = getopt (argc, argv, "hqd:b:")) != -1) + switch (c) { + case 'q': + verbose = 0; + break; + + case 'b': + dbspec = optarg; + break; + + case 'd': + dev = optarg; + break; + + default: + help(basename(argv[0])); + break; + } + + if (device_lock(dev, basename(argv[0])) != 0) + goto finish; + + locked = 1; + + if ((fd = modem_open(dev)) < 0) + goto finish; + + //"dbname=pgets user=pgets_fill password=mahatma" + if (!(db = db_connect(dbspec))) + goto finish; + + while (*dbspec) + *(dbspec++) = 'X'; + + signal(SIGINT, sigint); + siginterrupt(SIGINT, 0); + + flush_data(fd); + work(fd, db); + + r = 0; + +finish: + if (fd >= 0) + modem_close(fd); + + if (db) + db_disconnect(db); + + if (locked) + device_unlock(dev); + + return r; +} diff --git a/src/pgets.h b/src/pgets.h new file mode 100644 index 0000000..f1d34fa --- /dev/null +++ b/src/pgets.h @@ -0,0 +1,35 @@ +#ifndef foopgetshfoo +#define foopgetshfoo + +/* $Id$ + * + * This file is part of pgets. + * + * pgets is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * pgets is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with pgets; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +struct entry { + char remote_msn[22]; + int local_mm; + int participant; + int incoming; + int day; + int month; + int hour; + int minute; + int duration; +}; + +#endif diff --git a/src/postgres.c b/src/postgres.c new file mode 100644 index 0000000..090afaf --- /dev/null +++ b/src/postgres.c @@ -0,0 +1,93 @@ +/* $Id$ + * + * This file is part of pgets. + * + * pgets is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * pgets is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with pgets; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include +#include +#include + +#include + +#include "db.h" + +void* db_connect(const char*t) { + PGconn *pg; + + if (!t) { + fprintf(stderr, "Database specification required.\n"); + return NULL; + } + + if (!(pg = PQconnectdb(t))) + return NULL; + + if (PQstatus(pg) != CONNECTION_OK) { + fprintf(stderr, "Could not connect to database: %s\n", PQerrorMessage(pg)); + PQfinish(pg); + return NULL; + } + + return pg; +} + +void db_disconnect(void *db) { + assert(db); + PQfinish((PGconn*) db); +} + +int db_write_entry(void *db, const struct entry *entry) { + static time_t t = 0; + char query[512]; + struct tm tm; + PGresult* r; + PGconn *pg = db; + int year; + + assert(pg); + + if (t == 0) { + t = time(NULL); + localtime_r(&t, &tm); + } + + if (entry->month < tm.tm_mon+1 || (entry->month == tm.tm_mon+1 && entry->day <= tm.tm_mday)) + year = 1900+tm.tm_year; + else + year = 1900+tm.tm_year-1; + + snprintf(query, sizeof(query), "INSERT INTO pgets_accounting (remote_msn, local_mm, participant, incoming, _timestamp, duration) VALUES ('%s', %i, %i, '%c', TIMESTAMP '%04i-%02i-%02i %02i:%02i:00', %i)", + entry->remote_msn, entry->local_mm, entry->participant, entry->incoming ? 't' : 'f', year, entry->month, entry->day, entry->hour, entry->minute, entry->duration); + + if (!(r = PQexec(pg, query)) || PQstatus(pg) != CONNECTION_OK || PQresultStatus(r) != PGRES_COMMAND_OK) { + if (r) { + if (PQresultStatus(r) == PGRES_FATAL_ERROR) { + PQclear(r); + return 1; + } + + fprintf(stderr, "Query [%s] failed (#1), reason given: %s\n", query, PQresultErrorMessage(r)); + PQclear(r); + } else + fprintf(stderr, "Query [%s] failed (#2), reason given: %s\n", query, PQerrorMessage(pg)); + + return -1; + } + + PQclear(r); + return 0; +} diff --git a/src/sqlite.c b/src/sqlite.c new file mode 100644 index 0000000..ec9aef5 --- /dev/null +++ b/src/sqlite.c @@ -0,0 +1,98 @@ +/* $Id$ + * + * This file is part of pgets. + * + * pgets is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * pgets is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with pgets; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include +#include +#include +#include +#include + +#include + +#include "db.h" + + +static int busy(void *v, const char *name, int try) { + fprintf(stderr, "Table '%s' locked (try #%i), sleeping 2s ... \r", name, try); + sleep(2); + return 0; +} + +void* db_connect(const char*t) { + sqlite *db; + char *e; + + if (!t) { + fprintf(stderr, "Database specification required.\n"); + return NULL; + } + + if (!(db = sqlite_open(t, 0, &e))) { + fprintf(stderr, "Failed to open database: %s\n", e); + free(e); + } + + sqlite_busy_handler(db, busy, NULL); + + return db; +} + +void db_disconnect(void *db) { + assert(db); + sqlite_close((sqlite*) db); +} + +int db_write_entry(void *vdb, const struct entry *entry) { + static time_t t = 0; + char query[512]; + struct tm tm; + sqlite *db = vdb; + int year; + char *e = NULL; + int ret; + + assert(db); + + if (t == 0) { + t = time(NULL); + localtime_r(&t, &tm); + } + + if (entry->month < tm.tm_mon+1 || (entry->month == tm.tm_mon+1 && entry->day <= tm.tm_mday)) + year = 1900+tm.tm_year; + else + year = 1900+tm.tm_year-1; + + snprintf(query, sizeof(query), "INSERT INTO pgets_accounting (remote_msn, local_mm, participant, incoming, _timestamp, duration) VALUES ('%s', %i, %i, '%c', '%04i-%02i-%02i %02i:%02i:00', %i)", + entry->remote_msn, entry->local_mm, entry->participant, entry->incoming ? 't' : 'f', year, entry->month, entry->day, entry->hour, entry->minute, entry->duration); + + if ((ret = sqlite_exec(db, query, NULL, NULL, &e)) != SQLITE_OK) { + + if (ret == SQLITE_CONSTRAINT) { + free(e); + return 1; + } + + fprintf(stderr, "sqlite_exec(): %s\n", e); + free(e); + return -1; + } + + return 0; +} diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000..d6a5ad5 --- /dev/null +++ b/src/util.c @@ -0,0 +1,102 @@ +/* $Id$ + * + * This file is part of pgets. + * + * pgets is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * pgets is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with pgets; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include +#include +#include + +#include "util.h" + +ssize_t loop_read (int FILEDES, void *BUFFER, size_t SIZE) { + ssize_t c = 0; + + while (SIZE > 0) { + ssize_t r = read(FILEDES, BUFFER, SIZE); + + if (r <= 0) { + if (c == 0) + return r; + else + return c; + } + + SIZE -= r; + c += r; + BUFFER += r; + } + + return c; +} + +ssize_t loop_write(int FILEDES, const void *BUFFER, size_t SIZE) { + ssize_t c = 0; + + while (SIZE > 0) { + ssize_t r = write(FILEDES, BUFFER, SIZE); + + if (r <= 0) { + if (c == 0) + return r; + else + return c; + } + + SIZE -= r; + c += r; + BUFFER += r; + } + + return c; +} + +// The name says it all. +void flush_data(int fd) { + tcflush(fd, TCIFLUSH); + tcflush(fd, TCIFLUSH); +} + +ssize_t read_line(int FILEDES, char *ln, size_t SIZE) { + ssize_t c = 0; + + while (SIZE > 0) { + ssize_t r; + + if ((r = read(FILEDES, ln, 1)) != 1) { + if (r < 0 && !c) + return -1; + + break; + } + + SIZE--; + c++; + + if (*ln == '\r' || *ln == '\n') { + ln++; + break; + } + + ln++; + } + + if (SIZE) + *ln = 0; + + return c; +} diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..2ac3eb7 --- /dev/null +++ b/src/util.h @@ -0,0 +1,35 @@ +#ifndef fooutilhfoo +#define fooutilhfoo + +/* $Id$ + * + * This file is part of pgets. + * + * pgets is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * pgets is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with pgets; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +/* A loop of write()s */ +ssize_t loop_write(int FILEDES, const void *BUFFER, size_t SIZE); + +/* A loop of read()s */ +ssize_t loop_read(int FILEDES, void *BUFFER, size_t SIZE); + +/* Read a line */ +ssize_t read_line(int FIELDES, char *ln, size_t SIZE); + +/* Flush all waiting data */ +void flush_data(int fd); + +#endif -- cgit