summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2003-07-27 19:18:57 +0000
committerLennart Poettering <lennart@poettering.net>2003-07-27 19:18:57 +0000
commit91263ea95ec16bb376245c6931ea28d65bdcdc88 (patch)
treee4caa5a43070c71fb016f8cb31dc81e6551f2b16 /src
Moved to standard repository layout
git-svn-id: file:///home/lennart/svn/public/ivcall/trunk@2 e0b13411-74c3-0310-b366-a0654dd0340f
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am21
-rw-r--r--src/ivcall.c729
-rw-r--r--src/lock.c130
-rw-r--r--src/lock.h28
-rw-r--r--src/util.c65
-rw-r--r--src/util.h28
6 files changed, 1001 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..1fa04d5
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,21 @@
+# $Id$
+
+# This file is part of ifplugd.
+#
+# ifplugd 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.
+#
+# ifplugd 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 ifplugd; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+
+bin_PROGRAMS = ivcall
+ivcall_SOURCES = ivcall.c lock.c lock.h util.c util.h
+
diff --git a/src/ivcall.c b/src/ivcall.c
new file mode 100644
index 0000000..54c840d
--- /dev/null
+++ b/src/ivcall.c
@@ -0,0 +1,729 @@
+/* $Id$ */
+
+/*
+ * This file is part of ivcall.
+ *
+ * ivcall 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.
+ *
+ * ivcall 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 ivcall; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <termios.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <getopt.h>
+#include <string.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/select.h>
+
+#include "util.h"
+#include "lock.h"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+// Where do lockfiles reside?
+#ifndef LOCK_PATH
+#define LOCK_PATH "/var/lock"
+#endif
+
+// Those nifty DLE-sequences
+#define DLE 0x10
+#define ETX 0x03
+#define DC4 0x14
+
+// Baudrate for the TTY. Should be greater the 64000.
+#define BAUD_RATE B115200
+
+// Every single commands gets a timeout of 2 seconds.
+#define COMMAND_TIMEOUT 2
+
+// The default timeout for waiting that the call gets accepted.
+#define DEFAULT_TIMEOUT 30
+
+// Initialisation of the modem
+const char *at_commands[] = {
+ "AT&F\n", "OK\r\n",
+ "ATI\n", "Linux ISDN\r\nOK",
+ "AT&B128\n", "OK\r\n",
+ "AT+FCLASS=8\n", "OK\r\n",
+ "AT+VSM=6\n", "OK\r\n",
+ "ATS18=1\n", "OK\r\n",
+ "ATS14=4\n", "OK\r\n",
+ "ATS13.4=1\n", "OK\r\n",
+ "ATS23=1\n", "OK\r\n",
+ 0
+};
+
+// Local MSN
+char *source_msn = NULL;
+
+// Remote MSN
+char *destination_msn = NULL;
+
+// Modem-device
+char *device = NULL;
+
+// Our name
+char *appname = NULL;
+
+// The default timeout
+int timeout = DEFAULT_TIMEOUT;
+
+// Enable debug-mode or not?
+int debug = 0;
+
+// We got a ctrl-c
+volatile int interrupted = 0;
+
+// Got an SIGARLM?
+volatile int alarmed = 0;
+
+// Shall we wait for a call?
+int answer = 0;
+
+// After how many RINGs we shall answer?
+int rings = 1;
+
+// Saved TTY configuration
+struct termios saved_termios;
+
+// Our ctrl-c handler
+void sigint(int sig) {
+ const char *s = "Got SIGINT\n";
+ write(2, s, strlen(s));
+ interrupted = 1;
+}
+
+void sigalrm(int sig) {
+ const char *s = "Timeout\n";
+ write(2, s, strlen(s));
+ alarmed = 1;
+}
+
+// Waits for a certain string from the modem up to a certain time.
+int tty_waitfor(int fd, const char *what, int t) {
+ static char buf[256];
+ char *p = buf;
+ int r = -1, l;
+
+ if (debug)
+ fprintf(stderr, "<- ");
+
+ l = strlen(what);
+
+ if (t == -1)
+ t = 0;
+ alarm(0);
+ alarmed = 0;
+ alarm(t);
+
+ while (!interrupted && !alarmed) {
+ ssize_t s;
+
+ if ((s = read(fd, p, 1)) <= 0)
+ break;
+
+ if (debug)
+ fprintf(stderr, "%c", *p);
+
+ p++;
+
+ if (p - buf >= l && !strncmp(p-l, what, l)) {
+ r = 0;
+ break;
+ }
+ }
+
+
+ alarm(0);
+ alarmed = 0;
+
+ if (debug)
+ fprintf(stderr, "\n");
+
+ return r;
+}
+
+// Write a string to the modem
+int tty_puts(int fd, const char *s) {
+ int l = strlen(s);
+
+ if (loop_write(fd, s, l) != l)
+ return -1;
+
+ if (tcdrain(fd) < 0)
+ return -1;
+
+ if (debug)
+ fprintf(stderr, "-> %s", s);
+
+ return 0;
+}
+
+// Issue a command to the modem an wait for an answer
+int tty_command(int fd, const char *cmd, const char* resp, int t) {
+ tcflush(fd, TCIFLUSH);
+ tty_puts(fd, cmd);
+ return tty_waitfor(fd, resp, t);
+}
+
+// Hang up the modem
+int hard_hangup(int fd) {
+ struct termios pts;
+
+ tcflush(fd, TCIOFLUSH);
+
+ if (tcgetattr(fd, &pts) < 0)
+ return -1;
+
+ cfsetospeed(&pts, B0);
+ cfsetispeed(&pts, B0);
+ tcsetattr(fd, TCSANOW, &pts);
+
+ sleep(1);
+
+ if (tcgetattr(fd, &pts) < 0)
+ return -1;
+ cfsetospeed(&pts, BAUD_RATE);
+ cfsetispeed(&pts, BAUD_RATE);
+ tcsetattr(fd, TCSANOW, &pts);
+
+ sleep(1);
+
+ tcflush(fd, TCIFLUSH);
+
+ return 0;
+}
+
+// Opens the modem and sets the baud rate
+int tty_open(const char *device) {
+ struct termios pts, pts2;
+ int fd = -1, n;
+
+ if ((fd = open(device, O_RDWR|O_NDELAY)) < 0)
+ goto fail;
+
+ if ((n = fcntl(fd, F_GETFL, 0)) < 0)
+ goto fail;
+
+ if (fcntl(fd, F_SETFL, n & ~O_NDELAY) < 0)
+ goto fail;
+
+ if (tcgetattr(fd, &saved_termios) < 0)
+ goto fail;
+
+ memset(&pts, 0, sizeof(pts));
+ pts.c_cflag = CRTSCTS | IGNPAR | HUPCL | CS8;
+ pts.c_iflag = IGNPAR;
+ pts.c_oflag = 0;
+ pts.c_lflag = 0;
+
+ pts.c_cc[VMIN] = 1;
+ pts.c_cc[VTIME] = 0;
+
+ cfsetospeed(&pts, BAUD_RATE);
+ cfsetispeed(&pts, BAUD_RATE);
+
+ tcflush(fd, TCIOFLUSH);
+ if (!tcsetattr(fd, TCSANOW, &pts))
+ if (!tcgetattr(fd, &pts2))
+ if (!memcmp(&pts, &pts, sizeof(struct termios)))
+ return fd;
+
+ tcsetattr(fd, TCSANOW, &saved_termios);
+
+fail:
+
+ if (fd >= 0)
+ close(fd);
+
+ return -1;
+}
+
+// Closes the modem device and resets the baudrate
+void tty_close(int fd) {
+ tcflush(fd, TCIOFLUSH);
+ tcsetattr(fd, TCSANOW, &saved_termios);
+ close(fd);
+}
+
+// Detects the DCD line
+int detect_carrier(int fd) {
+ unsigned int arg;
+
+ if (ioctl(fd, TIOCMGET, &arg) < 0)
+ return -1;
+
+ return arg & TIOCM_CAR ? 1 : 0;
+}
+
+// Filters out DLE-sequences from modem
+char *filter_dle(unsigned char *data, int *size, int *flag, int *quit) {
+ unsigned char *ret, *m, *s, *d;
+ int l;
+
+ if (!(ret = malloc(*size)))
+ return NULL;
+
+ for (l = *size, s = data, d = ret; l > 0; l--, s++) {
+ if (*flag) {
+ switch (*s) {
+ case DLE :
+ *(d++) = DLE;
+ break;
+
+ case ETX :
+ case DC4 :
+ *quit = 1;
+ break;
+
+ default :
+ // We recieved some DTMF signals, but we ignore them.
+ break;
+ }
+
+ *flag = 0;
+ continue;
+ }
+
+ if (*s == DLE)
+ *flag = 1;
+ else
+ *(d++) = *s;
+
+ }
+
+ *size = d-ret;
+
+ if (!(m = realloc(ret, *size))) {
+ free(ret);
+ return NULL;
+ }
+
+ return m;
+}
+
+// Escape DLE-bytes for sending data to the modem
+char* escape_dle(unsigned char *data, int *size) {
+ unsigned char *ret, *m, *s, *d;
+ int l, ms;
+
+ if (!(ret = malloc(ms = (int) (*size*1.05))))
+ return NULL;
+
+ for (l = *size, s = data, d = ret; l > 0; l--, s++) {
+
+ if (*s == DLE) {
+ *(d++) = DLE;
+ (*size)++;
+
+ if (ms < *size) {
+ int n = d-ret;
+
+ fprintf(stderr, "REALLOC!\n");
+
+ if (!(m = realloc(ret, ms = (int) (*size*1.05)))) {
+ free(ret);
+ return NULL;
+ }
+ ret = m;
+ d = ret+n;
+ }
+ }
+
+ *(d++) = *s;
+ }
+
+ if (!(m = realloc(ret, *size))) {
+ free(ret);
+ return NULL;
+ }
+
+ return m;
+}
+
+
+// The function that does the modem work
+int go(void) {
+ int i, flag, warned = 0, r = -1, fd = -1, quit;
+ char *incoming = NULL, *outgoing = NULL;
+ int incoming_idx = 0, incoming_len = 0,
+ outgoing_idx = 0, outgoing_len = 0;
+ int fm;
+ char fn[PATH_MAX];
+ static char buf[1024];
+
+ if ((device ? device_lock(device, appname) : device_lock_first(appname, fn, sizeof(fn))) < 0) {
+ fprintf(stderr, "Could not get lock on device: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ if (!device)
+ if (!(device = strdup(fn))) {
+ fprintf(stderr, "Out of memory!\n");
+ goto fail;
+ }
+
+ if ((fd = tty_open(device)) < 0) {
+ fprintf(stderr, "Could not open device: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ for (i = 0; at_commands[i] != 0; i+=2)
+ if (tty_command(fd, at_commands[i], at_commands[i+1], COMMAND_TIMEOUT) < 0) {
+ fprintf(stderr, "Initialisation failure.\n");
+ goto fail;
+ }
+
+ snprintf(buf, sizeof(buf), "AT&E%s\n", source_msn);
+ if (tty_command(fd, buf, "OK\r\n", COMMAND_TIMEOUT) < 0) {
+ fprintf(stderr, "Initialisation failure.\n");
+ goto fail;
+ }
+
+ if (answer)
+ snprintf(buf, sizeof(buf), "ATS0=%i\n", rings);
+ else
+ snprintf(buf, sizeof(buf), "ATD%s\n", destination_msn);
+
+ if (tty_command(fd, buf, "VCON\r\n", timeout) < 0) {
+ fprintf(stderr, "Failed to accept connection.\n");
+ r = -2;
+ goto fail;
+ }
+
+ if (tty_command(fd, "AT+VTX+VRX\n", "CONNECT\r\n\r\nCONNECT\r\n", COMMAND_TIMEOUT) < 0) {
+ fprintf(stderr, "Could not switch to full-duplex audio: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ flag = quit= 0;
+
+ if (fd > 1)
+ fm = fd+1;
+ else
+ fm = 1+1;
+
+ while (!interrupted && (!quit || outgoing || incoming)) {
+ fd_set rfds, wfds;
+ int c, b;
+
+ if ((c = detect_carrier(fd)) < 0) {
+ fprintf(stderr, "Failure reading DCD line: %s\n", strerror(errno));
+ goto fail;
+ } else if (!c) {
+ if (outgoing) {
+ free(outgoing);
+ outgoing = NULL;
+ }
+ quit = 1;
+ break;
+ }
+
+ b = 0;
+ FD_ZERO(&rfds);
+ if (!quit) {
+ if (!outgoing) {
+ FD_SET(0, &rfds);
+ b = 1;
+ }
+ if (!incoming) {
+ b = 1;
+ FD_SET(fd, &rfds);
+ }
+ }
+
+ FD_ZERO(&wfds);
+ if (outgoing) {
+ b=1;
+ FD_SET(fd, &wfds);
+ }
+ if (incoming) {
+ b = 1;
+ FD_SET(1, &wfds);
+ }
+
+ if (!b)
+ break;
+
+ if ((r = select(fm, &rfds, &wfds, 0, 0)) < 0) {
+ if (errno == EINTR)
+ continue;
+
+ fprintf(stderr, "select(): %s\n", strerror(errno));
+ goto fail;
+ }
+
+
+ if (!outgoing && FD_ISSET(0, &rfds)) {
+ if ((outgoing_len = read(0, buf, sizeof(buf))) < 0) {
+ fprintf(stderr, "read(): %s\n", strerror(errno));
+ goto fail;
+ }
+
+ if (!outgoing_len)
+ quit = 1;
+ else {
+ outgoing = escape_dle(buf, &outgoing_len);
+ outgoing_idx = 0;
+ }
+ }
+
+ if (outgoing && FD_ISSET(fd, &wfds)) {
+ ssize_t l;
+ if ((l = write(fd, outgoing+outgoing_idx, outgoing_len-outgoing_idx)) < 0) {
+ fprintf(stderr, "write(): %s\n", strerror(errno));
+ goto fail;
+ }
+
+ if (!l)
+ quit = 1;
+ else {
+ outgoing_idx += l;
+
+ if (outgoing_idx >= outgoing_len) {
+ free(outgoing);
+ outgoing = NULL;
+ outgoing_len = outgoing_idx = 0;
+ }
+ }
+ }
+
+
+ if (!incoming && FD_ISSET(fd, &rfds)) {
+ if ((incoming_len = read(fd, buf, sizeof(buf))) < 0) {
+ fprintf(stderr, "read(): %s\n", strerror(errno));
+ goto fail;
+ }
+
+ if (!incoming_len)
+ quit = 1;
+ else {
+ incoming = filter_dle(buf, &incoming_len, &flag, &quit);
+ incoming_idx = 0;
+ }
+ }
+
+ if (incoming && FD_ISSET(1, &wfds)) {
+ ssize_t l;
+ if (isatty(1)) {
+ if (!warned) {
+ fprintf(stderr, "I won't print out binary data to a tty. Pipe stdout to somewhere else.\n");
+ warned = 1;
+ }
+
+ free(incoming);
+ incoming = NULL;
+ incoming_len = incoming_idx = 0;
+
+ } else {
+ if ((l = write(1, incoming+incoming_idx, incoming_len-incoming_idx)) < 0) {
+ fprintf(stderr, "write(1): %s\n", strerror(errno));
+ goto fail;
+ }
+
+ if (!l)
+ quit = 1;
+ else {
+ incoming_idx += l;
+
+ if (incoming_idx >= incoming_len) {
+ free(incoming);
+ incoming = NULL;
+ incoming_len = incoming_idx = 0;
+ }
+ }
+ }
+ }
+ }
+
+
+ buf[0] = DLE;
+ buf[1] = DC4;
+ buf[2] = DLE;
+ buf[3] = ETX;
+ loop_write(fd, buf, 4);
+ tcdrain(fd);
+ tty_puts(fd, "ATH\n");
+
+ r = 0;
+
+fail:
+
+ if (incoming)
+ free(incoming);
+
+ if (outgoing)
+ free(outgoing);
+
+ if (fd >= 0) {
+ hard_hangup(fd);
+ tty_puts(fd, "AT&F\n");
+ tty_close(fd);
+ device_unlock();
+ }
+
+ return r;
+}
+
+struct option long_options[] = {
+ {"help", 0, 0, 'h'},
+ {"timeout", 1, 0, 't'},
+ {"device", 1, 0, 'd'},
+ {"verbose", 0, 0, 'v'},
+ {"answer", 0, 0, 'a'},
+ {"ring", 1, 0, 'r'},
+ {"version", 0, 0, 'V'},
+ {0, 0, 0, 0}
+};
+
+// Most important part of the program
+void usage() {
+ printf("Usage: %s [options] <source-msn> <destination-msn>\n"
+ " %s -a [options] <msn>\n"
+ "\n"
+ "Options:\n"
+ " -h, --help Shows this usage information\n"
+ " -t, --timeout=TIMEOUT Sets a timeout (%i)\n"
+ " -d, --device=DEVICE Selects a device (if not specified, the first available /dev/ttyInn is used)\n"
+ " -v, --verbose Enables debug mode (%s)\n"
+ " -a, --answer Wait for incoming call instead of calling out (%s)\n"
+ " -r, --ring=RINGS On incoming call answer after given RINGs (%i)\n"
+ " -V, --version Show %s version number\n",
+ appname, appname,
+ timeout,
+ debug ? "on" : "off",
+ answer ? "on" : "off",
+ rings,
+ appname);
+}
+
+// Parses command line and executes go()
+int main (int argc, char *argv[]) {
+ int c, r = 1;
+
+ appname = basename(argv[0]);
+
+ while((c = getopt_long(argc, argv, "ht:d:var:V", long_options, NULL)) >= 0) {
+
+ switch (c) {
+ case 'V' :
+ printf("%s "VERSION"\n", appname);
+ r = 0;
+ goto finish;
+
+ case 'h' :
+ usage();
+ r = 0;
+ goto finish;
+
+ case 't' :
+ if (optarg) {
+ if ((timeout = atoi(optarg)) < 1) {
+ fprintf(stderr, "Timeout must be >= 1.\n");
+ goto finish;
+ }
+ } else {
+ fprintf(stderr, "Expected argument on timeout option.\n");
+ goto finish;
+ }
+ break;
+
+ case 'd' :
+ if (optarg) {
+ if (device)
+ free(device);
+ else
+ device = strdup(optarg);
+ } else {
+ fprintf(stderr, "Expected argument on device option.\n");
+ goto finish;
+ }
+ break;
+
+ case 'v' :
+ debug = !debug;
+ break;
+
+ case 'a' :
+ answer = !answer;
+ break;
+
+ case 'r' :
+ if (optarg) {
+
+ if ((rings = atoi(optarg)) < 1) {
+ fprintf(stderr, "RINGs must be >= 1.\n");
+ goto finish;
+ }
+ } else {
+ fprintf(stderr, "Expected argument on rings option.\n");
+ goto finish;
+ }
+ break;
+
+ case '?' :
+ goto finish;
+ }
+ }
+
+ if (answer) {
+ if (optind != argc-1) {
+ fprintf(stderr, "You did not specify the listening MSN.\n");
+ usage();
+ goto finish;
+ }
+
+ source_msn = strdup(argv[optind]);
+ } else {
+ if (optind != argc-2) {
+ fprintf(stderr, "You did not specify both destination and source MSNs.\n");
+ usage();
+ goto finish;
+ }
+
+ source_msn = strdup(argv[optind]);
+ destination_msn = strdup(argv[optind+1]);
+ }
+
+ if (!timeout)
+ timeout = answer ? -1 : DEFAULT_TIMEOUT;
+
+ signal(SIGINT, &sigint);
+ signal(SIGALRM, &sigalrm);
+ signal(SIGPIPE, SIG_IGN);
+ siginterrupt(SIGINT, 1);
+ siginterrupt(SIGALRM, 1);
+
+ r = go() < 0 ? 1 : 0;
+
+finish:
+
+ if (source_msn) free (source_msn);
+ if (destination_msn) free(destination_msn);
+ if (device) free(device);
+
+ return r;
+}
+
diff --git a/src/lock.c b/src/lock.c
new file mode 100644
index 0000000..465736c
--- /dev/null
+++ b/src/lock.c
@@ -0,0 +1,130 @@
+/* $Id$ */
+
+/*
+ * This file is part of ivcall.
+ *
+ * ivcall 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.
+ *
+ * ivcall 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 ivcall; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <unistd.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <pwd.h>
+
+#include "lock.h"
+#include "util.h"
+
+// Where do lockfiles reside?
+#define LOCK_PATH "/var/lock"
+
+static char* lockfile = NULL;
+
+// Tries to lock a TTY-device, returns 0 on success
+int device_lock(char *dev, char *appname) {
+ char buf[256], u[16];
+ char path[PATH_MAX];
+ int fd;
+ struct passwd *pwd;
+ char *username;
+
+ assert(dev && !lockfile);
+
+ if (access(LOCK_PATH, W_OK) < 0)
+ return -1;
+
+ snprintf(path, sizeof(path), "%s/LCK..%s", LOCK_PATH, basename(dev));
+
+ if ((fd = open(path, O_RDONLY)) >= 0) {
+ int n = loop_read(fd, buf, sizeof(buf) - 1);
+ close(fd);
+
+ if (n > 0) {
+ int pid;
+
+ if (n == 4)
+ pid = *(int*) buf;
+ else {
+ buf[n] = 0;
+ sscanf(buf, "%d", &pid);
+ }
+
+ if (pid > 0) {
+ if (kill((pid_t) pid, 0) < 0 && errno == ESRCH) {
+ fprintf(stderr, "Lockfile is stale. Overriding it.\n");
+ unlink(path);
+ } else
+ return -1;
+ }
+ }
+ }
+
+ if ((fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0666)) < 0)
+ return -1;
+
+ if ((pwd = getpwuid(getuid())))
+ username = pwd->pw_name;
+ else
+ snprintf(username = u, sizeof(u), "%u", getuid());
+
+ snprintf(buf, sizeof(buf), "%10ld %s %.20s\n", (long)getpid(), appname, username);
+
+ if (loop_write(fd, buf, strlen(buf)) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+
+ lockfile = strdup(path);
+
+ return 0;
+}
+
+// Tries to lock the first free device, returns 0 on success
+int device_lock_first(char *appname, char *dev, size_t l) {
+ int i, found = 0;
+
+ for (i = 2; i <= 20; i++) {
+ snprintf(dev, l, "/dev/ttyI%i", i);
+
+ if (!device_lock(dev, appname)) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (found)
+ return 0;
+ else {
+ fprintf(stderr, "Could not find free device.\n");
+ return -1;
+ }
+}
+
+// Deletes the lockfile
+void device_unlock(void) {
+ if (lockfile) {
+ unlink(lockfile);
+ free (lockfile);
+ lockfile = NULL;
+ }
+}
diff --git a/src/lock.h b/src/lock.h
new file mode 100644
index 0000000..a1847c9
--- /dev/null
+++ b/src/lock.h
@@ -0,0 +1,28 @@
+#ifndef foolockhfoo
+#define foolockhfoo
+
+/* $Id$ */
+
+/*
+ * This file is part of ivcall.
+ *
+ * ivcall 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.
+ *
+ * ivcall 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 ivcall; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+int device_lock(char *dev, char*appname);
+int device_lock_first(char*appname, char *dev, size_t l);
+void device_unlock(void);
+
+#endif
diff --git a/src/util.c b/src/util.c
new file mode 100644
index 0000000..153a5c4
--- /dev/null
+++ b/src/util.c
@@ -0,0 +1,65 @@
+/* $Id$ */
+
+/*
+ * This file is part of ivcall.
+ *
+ * ivcall 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.
+ *
+ * ivcall 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 ivcall; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <unistd.h>
+#include <termios.h>
+#include <string.h>
+
+#include "util.h"
+
+ssize_t loop_read (int FILEDES, const void *BUFFER, size_t SIZE) {
+ int c = 0;
+
+ while (SIZE > 0) {
+ int r;
+
+ if ((r = read(FILEDES, (void*) BUFFER, SIZE)) <= 0)
+ break;
+
+ SIZE -= r;
+ c += r;
+ BUFFER = ((void*) (((char*) BUFFER) + r));
+ }
+
+ return c;
+}
+
+ssize_t loop_write(int FILEDES, const void *BUFFER, size_t SIZE) {
+ int c = 0;
+
+ while (SIZE > 0) {
+ int r;
+
+ if ((r = write(FILEDES, BUFFER, SIZE)) <= 0)
+ break;
+
+ SIZE -= r;
+ c += r;
+ BUFFER = ((void*) (((char*) BUFFER) + r));
+ }
+
+ return c;
+}
+
+// Returns a pointer to the filename in a path
+char *basename(char *f) {
+ char *i = strrchr(f, '/');
+ return i ? i+1 : f;
+}
diff --git a/src/util.h b/src/util.h
new file mode 100644
index 0000000..1192ad4
--- /dev/null
+++ b/src/util.h
@@ -0,0 +1,28 @@
+#ifndef fooutilhfoo
+#define fooutilhfoo
+
+/* $Id$ */
+
+/*
+ * This file is part of ivcall.
+ *
+ * ivcall 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.
+ *
+ * ivcall 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 ivcall; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+char *basename(char *f);
+ssize_t loop_write(int FILEDES, const void *BUFFER, size_t SIZE);
+ssize_t loop_read (int FILEDES, const void *BUFFER, size_t SIZE);
+
+#endif