<p>laforge has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.osmocom.org/c/libosmocore/+/17242">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">Import 'neocon' terminal program from Openmoko<br><br>This imports the latest version of neocon from commit<br>504449a8c707fe9e93b670e024db3a315ce47a26 of<br>https://github.com/laf0rge/neocon.git.<br><br>neocon is a small and simple terminal program, similar to the venerable 'cu'<br>that is part of (Taylor) UUCP for decades.  The specialty of neocon is that it<br>can deal with disappearing and re-appearing devices by simply re-opening them<br>once they re-appear.  This is e.g. happening on USB unplug/replug of a USB-serial<br>adapter, or for example during reboot of virtual serial ports of cellular modems.<br><br>We import this to libosmcoore as the program is too small to warrant<br>separate git repo + packaging.  In Osmocom we often deal with [virtual]<br>serial ports of all kinds of devices, hence it is useful to have it<br>around.<br><br>The name 'neocon' is a reference to the Neo1937, the first open source Linux<br>smartphone developed by Openmoko in 2007.  neocon was created during Neo1973<br>development by Werner Almesberger.<br><br>Change-Id: I4f4f0dae6774447eb370fc1488a4689b1d5d54f8<br>---<br>M debian/control<br>M debian/copyright<br>A debian/neocon.install<br>M utils/Makefile.am<br>A utils/neocon.c<br>5 files changed, 457 insertions(+), 1 deletion(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.osmocom.org:29418/libosmocore refs/changes/42/17242/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/debian/control b/debian/control</span><br><span>index cdf26bb..03dfbde 100644</span><br><span>--- a/debian/control</span><br><span>+++ b/debian/control</span><br><span>@@ -359,3 +359,20 @@</span><br><span>  were originally developed as part of the OpenBSC project, but which are of a</span><br><span>  more generic nature and thus useful to (at least) other programs that are</span><br><span>  developed in the sphere of Free Software / Open Source mobile communication.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Package: neocon</span><br><span style="color: hsl(120, 100%, 40%);">+Architecture: any</span><br><span style="color: hsl(120, 100%, 40%);">+Section: utils</span><br><span style="color: hsl(120, 100%, 40%);">+Depends: ${shlibs:Depends},</span><br><span style="color: hsl(120, 100%, 40%);">+       ${misc:Depends}</span><br><span style="color: hsl(120, 100%, 40%);">+Multi-Arch: same</span><br><span style="color: hsl(120, 100%, 40%);">+Description: Simplistic serial terminal program supporting re-connect on hotplug</span><br><span style="color: hsl(120, 100%, 40%);">+ neocon is a small and simple terminal program, similar to the venerable 'cu'</span><br><span style="color: hsl(120, 100%, 40%);">+ that is part of (Taylor) UUCP for decades.  The specialty of neocon is that it</span><br><span style="color: hsl(120, 100%, 40%);">+ can deal with disappearing and re-appearing devices by simply re-opening them</span><br><span style="color: hsl(120, 100%, 40%);">+ once they re-appear.  This is e.g. happening on USB unplug/replug of a USB-serial</span><br><span style="color: hsl(120, 100%, 40%);">+ adapter, or for example during reboot of virtual serial ports of cellular modems.</span><br><span style="color: hsl(120, 100%, 40%);">+ .</span><br><span style="color: hsl(120, 100%, 40%);">+ The name 'neocon' is a reference to the Neo1937, the first open source Linux</span><br><span style="color: hsl(120, 100%, 40%);">+ smartphone developed by Openmoko in 2007.  neocon was created during Neo1973</span><br><span style="color: hsl(120, 100%, 40%);">+ development by Werner Almesberger.</span><br><span>diff --git a/debian/copyright b/debian/copyright</span><br><span>index b119569..378886a 100644</span><br><span>--- a/debian/copyright</span><br><span>+++ b/debian/copyright</span><br><span>@@ -35,6 +35,10 @@</span><br><span>            2014 Nils O. SelĂ„sdal <noselasd@fiane.dyndns.org></span><br><span> License: GPL-2+</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+Files: utils/neocon.c</span><br><span style="color: hsl(120, 100%, 40%);">+Copyright: 2007-2008 by OpenMoko, Inc.</span><br><span style="color: hsl(120, 100%, 40%);">+License: GPL-2+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> Files: src/gsm/gsm48_ie.c</span><br><span>        src/gsm/lapd_core.c</span><br><span>        src/gsm/lapdm.c</span><br><span>diff --git a/debian/neocon.install b/debian/neocon.install</span><br><span>new file mode 100644</span><br><span>index 0000000..57bc38e</span><br><span>--- /dev/null</span><br><span>+++ b/debian/neocon.install</span><br><span>@@ -0,0 +1 @@</span><br><span style="color: hsl(120, 100%, 40%);">+usr/bin/neocon</span><br><span>diff --git a/utils/Makefile.am b/utils/Makefile.am</span><br><span>index 653b719..d3646b9 100644</span><br><span>--- a/utils/Makefile.am</span><br><span>+++ b/utils/Makefile.am</span><br><span>@@ -5,7 +5,9 @@</span><br><span> </span><br><span> EXTRA_DIST = conv_gen.py conv_codes_gsm.py</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-bin_PROGRAMS = osmo-arfcn osmo-auc-gen osmo-config-merge</span><br><span style="color: hsl(120, 100%, 40%);">+bin_PROGRAMS = neocon osmo-arfcn osmo-auc-gen osmo-config-merge</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+neocon_SOURCES = neocon.c</span><br><span> </span><br><span> osmo_arfcn_SOURCES = osmo-arfcn.c</span><br><span> </span><br><span>diff --git a/utils/neocon.c b/utils/neocon.c</span><br><span>new file mode 100644</span><br><span>index 0000000..3c2859b</span><br><span>--- /dev/null</span><br><span>+++ b/utils/neocon.c</span><br><span>@@ -0,0 +1,432 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * neocon.c - An interface for changing tty devices</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Copyright (C) 2007, 2008 by OpenMoko, Inc.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Written by Werner Almesberger <werner@openmoko.org></span><br><span style="color: hsl(120, 100%, 40%);">+ * All Rights Reserved</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software; you can redistribute it and/or modify</span><br><span style="color: hsl(120, 100%, 40%);">+ * it under the terms of the GNU General Public License as published by</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Free Software Foundation; either version 2 of the License, or</span><br><span style="color: hsl(120, 100%, 40%);">+ * (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+ * but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(120, 100%, 40%);">+ * GNU General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * You should have received a copy of the GNU General Public License along</span><br><span style="color: hsl(120, 100%, 40%);">+ * with this program; if not, write to the Free Software Foundation, Inc.,</span><br><span style="color: hsl(120, 100%, 40%);">+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <stdlib.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <stdio.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <stdio.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <unistd.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <string.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <termios.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <fcntl.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <assert.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <sys/time.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <sys/select.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define MAX_BUF 2048</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static char *const *ttys;</span><br><span style="color: hsl(120, 100%, 40%);">+static int num_ttys;</span><br><span style="color: hsl(120, 100%, 40%);">+static int curr_tty = -1; /* start with first tty */</span><br><span style="color: hsl(120, 100%, 40%);">+static speed_t speed = B115200;</span><br><span style="color: hsl(120, 100%, 40%);">+static struct termios console, tty;</span><br><span style="color: hsl(120, 100%, 40%);">+static FILE *log = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+static int timestamp = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+static char escape = '~';</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct bps {</span><br><span style="color: hsl(120, 100%, 40%);">+    speed_t speed;</span><br><span style="color: hsl(120, 100%, 40%);">+    int bps;</span><br><span style="color: hsl(120, 100%, 40%);">+} bps_tab[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+    { B300,       300 },</span><br><span style="color: hsl(120, 100%, 40%);">+    { B1200,     1200 },</span><br><span style="color: hsl(120, 100%, 40%);">+    { B2400,     2400 },</span><br><span style="color: hsl(120, 100%, 40%);">+    { B9600,     9600 },</span><br><span style="color: hsl(120, 100%, 40%);">+    { B19200,   19200 },</span><br><span style="color: hsl(120, 100%, 40%);">+    { B38400,   38400 },</span><br><span style="color: hsl(120, 100%, 40%);">+    { B57600,   57600 },</span><br><span style="color: hsl(120, 100%, 40%);">+    { B115200, 115200 },</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef B230400</span><br><span style="color: hsl(120, 100%, 40%);">+    { B230400, 230400 },</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef B460800</span><br><span style="color: hsl(120, 100%, 40%);">+    { B460800, 460800 },</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef B500000</span><br><span style="color: hsl(120, 100%, 40%);">+    { B500000, 500000 },</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef B576000</span><br><span style="color: hsl(120, 100%, 40%);">+    { B576000, 576000 },</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef B921600</span><br><span style="color: hsl(120, 100%, 40%);">+    { B921600, 921600 },</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef B1000000</span><br><span style="color: hsl(120, 100%, 40%);">+    { B1000000, 1000000 },</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef B1152000</span><br><span style="color: hsl(120, 100%, 40%);">+    { B1152000, 1152000 },</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef B1500000</span><br><span style="color: hsl(120, 100%, 40%);">+    { B1500000, 1500000 },</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef B2000000</span><br><span style="color: hsl(120, 100%, 40%);">+    { B2000000, 2000000 },</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef B2500000</span><br><span style="color: hsl(120, 100%, 40%);">+    { B2500000, 2500000 },</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef B3000000</span><br><span style="color: hsl(120, 100%, 40%);">+    { B3000000, 3000000 },</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef B3500000</span><br><span style="color: hsl(120, 100%, 40%);">+    { B3500000, 3500000 },</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef B4000000</span><br><span style="color: hsl(120, 100%, 40%);">+    { B4000000, 4000000 },</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+    { 0, 0 }</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static speed_t bps_to_speed(int bps)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    const struct bps *p;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    for (p = bps_tab; p->bps; p++)</span><br><span style="color: hsl(120, 100%, 40%);">+       if (p->bps == bps)</span><br><span style="color: hsl(120, 100%, 40%);">+     return p->speed;</span><br><span style="color: hsl(120, 100%, 40%);">+    fprintf(stderr, "no such speed: %d bps\n", bps);</span><br><span style="color: hsl(120, 100%, 40%);">+    exit(1);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void make_raw(int fd, struct termios *old)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    struct termios t;</span><br><span style="color: hsl(120, 100%, 40%);">+    long flags;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (tcgetattr(fd, &t) < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ perror("tcgetattr");</span><br><span style="color: hsl(120, 100%, 40%);">+        exit(1);</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+    if (old)</span><br><span style="color: hsl(120, 100%, 40%);">+ *old = t;</span><br><span style="color: hsl(120, 100%, 40%);">+    cfmakeraw(&t);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (fd) {</span><br><span style="color: hsl(120, 100%, 40%);">+      t.c_iflag  &= ~(IXON | IXOFF);</span><br><span style="color: hsl(120, 100%, 40%);">+    t.c_cflag |= CLOCAL;</span><br><span style="color: hsl(120, 100%, 40%);">+  t.c_cflag &= ~CRTSCTS;</span><br><span style="color: hsl(120, 100%, 40%);">+    if (cfsetispeed(&t, speed) < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+          perror("cfsetispeed");</span><br><span style="color: hsl(120, 100%, 40%);">+      exit(1);</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+     if (cfsetospeed(&t, speed) < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+          perror("cfsetospeed");</span><br><span style="color: hsl(120, 100%, 40%);">+      exit(1);</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+    if (tcsetattr(fd, TCSANOW, &t) < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+    perror("tcsetattr");</span><br><span style="color: hsl(120, 100%, 40%);">+        exit(1);</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+    flags = fcntl(fd,F_GETFL);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (flags < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+      perror("fcntl F_GETFL");</span><br><span style="color: hsl(120, 100%, 40%);">+    exit(1);</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+    if (fcntl(fd,F_SETFL,flags & ~O_NONBLOCK) < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+  perror("fcntl F_GETFL");</span><br><span style="color: hsl(120, 100%, 40%);">+    exit(1);</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int open_next_tty(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    int i, fd = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    for (i = 0; i != num_ttys; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+       curr_tty = (curr_tty+1) % num_ttys;</span><br><span style="color: hsl(120, 100%, 40%);">+   fd = open(ttys[curr_tty], O_RDWR | O_NDELAY);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (fd >= 0)</span><br><span style="color: hsl(120, 100%, 40%);">+           break;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+    if (fd >= 0)</span><br><span style="color: hsl(120, 100%, 40%);">+        make_raw(fd, &tty);</span><br><span style="color: hsl(120, 100%, 40%);">+    return fd;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Return 1 if the user manually forces a device change.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int scan(const char *s, size_t len)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    static int state = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+    const char *p;</span><br><span style="color: hsl(120, 100%, 40%);">+    int res = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    for (p = s; p != s+len; p++)</span><br><span style="color: hsl(120, 100%, 40%);">+     switch (state) {</span><br><span style="color: hsl(120, 100%, 40%);">+          case 0:</span><br><span style="color: hsl(120, 100%, 40%);">+           if (*p == escape)</span><br><span style="color: hsl(120, 100%, 40%);">+                 state++;</span><br><span style="color: hsl(120, 100%, 40%);">+          else</span><br><span style="color: hsl(120, 100%, 40%);">+              state = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+                break;</span><br><span style="color: hsl(120, 100%, 40%);">+            case 1:</span><br><span style="color: hsl(120, 100%, 40%);">+           if (*p == '.')</span><br><span style="color: hsl(120, 100%, 40%);">+                    exit(0);</span><br><span style="color: hsl(120, 100%, 40%);">+          if (*p == 'n')</span><br><span style="color: hsl(120, 100%, 40%);">+                    res = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+          state = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+            break;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+    return res;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int write_log(const char *buf, ssize_t len)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    size_t wrote;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    wrote = fwrite(buf, 1, len, log);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (wrote == len)</span><br><span style="color: hsl(120, 100%, 40%);">+   return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+    fprintf(stderr, "write failed. closing log file.\n");</span><br><span style="color: hsl(120, 100%, 40%);">+    fclose(log);</span><br><span style="color: hsl(120, 100%, 40%);">+    log = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+    return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int add_timestamp(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    struct timeval tv;</span><br><span style="color: hsl(120, 100%, 40%);">+    char buf[40]; /* be generous */</span><br><span style="color: hsl(120, 100%, 40%);">+    int len;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (gettimeofday(&tv, NULL) < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+        perror("gettimeofday");</span><br><span style="color: hsl(120, 100%, 40%);">+     exit(1);</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+    len = sprintf(buf, "%lu.%06lu ",</span><br><span style="color: hsl(120, 100%, 40%);">+      (unsigned long) tv.tv_sec, (unsigned long) tv.tv_usec);</span><br><span style="color: hsl(120, 100%, 40%);">+    return write_log(buf, len);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void do_log(const char *buf, ssize_t len)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    static int nl = 1; /* we're at the beginning of a new line */</span><br><span style="color: hsl(120, 100%, 40%);">+    char tmp[MAX_BUF];</span><br><span style="color: hsl(120, 100%, 40%);">+    const char *from;</span><br><span style="color: hsl(120, 100%, 40%);">+    char *to;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    assert(len <= MAX_BUF);</span><br><span style="color: hsl(120, 100%, 40%);">+    from = buf;</span><br><span style="color: hsl(120, 100%, 40%);">+    to = tmp;</span><br><span style="color: hsl(120, 100%, 40%);">+    while (from != buf+len) {</span><br><span style="color: hsl(120, 100%, 40%);">+     if (*from == '\r') {</span><br><span style="color: hsl(120, 100%, 40%);">+      from++;</span><br><span style="color: hsl(120, 100%, 40%);">+       continue;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+     if (nl && timestamp)</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!add_timestamp())</span><br><span style="color: hsl(120, 100%, 40%);">+             return;</span><br><span style="color: hsl(120, 100%, 40%);">+       nl = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+       if (*from == '\n') {</span><br><span style="color: hsl(120, 100%, 40%);">+      *to++ = *from++;</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!write_log(tmp, to-tmp))</span><br><span style="color: hsl(120, 100%, 40%);">+              return;</span><br><span style="color: hsl(120, 100%, 40%);">+           to = tmp;</span><br><span style="color: hsl(120, 100%, 40%);">+     nl = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+       continue;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+     *to++ = *from < ' ' || *from > '~' ? '#' : *from;</span><br><span style="color: hsl(120, 100%, 40%);">+       from++;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+    write_log(tmp, to-tmp);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int copy(int in, int out, int from_user, int single)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    char buffer[MAX_BUF];</span><br><span style="color: hsl(120, 100%, 40%);">+    ssize_t got, wrote, pos;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    got = read(in, buffer, single ? 1 : sizeof(buffer));</span><br><span style="color: hsl(120, 100%, 40%);">+    if (got <= 0)</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+    if (from_user) {</span><br><span style="color: hsl(120, 100%, 40%);">+       if (scan(buffer, got))</span><br><span style="color: hsl(120, 100%, 40%);">+            return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+    else {</span><br><span style="color: hsl(120, 100%, 40%);">+      if (log)</span><br><span style="color: hsl(120, 100%, 40%);">+          do_log(buffer, got);</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+    for (pos = 0; pos != got; pos += wrote) {</span><br><span style="color: hsl(120, 100%, 40%);">+        wrote = write(out, buffer+pos, got-pos);</span><br><span style="color: hsl(120, 100%, 40%);">+      if (wrote < 0)</span><br><span style="color: hsl(120, 100%, 40%);">+         return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+    return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void write_string(const char *s)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    int len = strlen(s);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    while (len) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ssize_t wrote;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      wrote = write(1, s, len);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (wrote < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+       perror("write");</span><br><span style="color: hsl(120, 100%, 40%);">+            exit(1);</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+     s += wrote;</span><br><span style="color: hsl(120, 100%, 40%);">+   len -= wrote;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void cleanup(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    if (tcsetattr(0, TCSANOW, &console) < 0)</span><br><span style="color: hsl(120, 100%, 40%);">+        perror("tcsetattr");</span><br><span style="color: hsl(120, 100%, 40%);">+    write(1, "\n", 1);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void usage(const char *name)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    fprintf(stderr,</span><br><span style="color: hsl(120, 100%, 40%);">+"usage: %s [-b bps] [-e escape] [-l logfile [-a] [-T]] [-t delay_ms] tty ...\n\n"</span><br><span style="color: hsl(120, 100%, 40%);">+"  -a           append to the log file if it already exists\n"</span><br><span style="color: hsl(120, 100%, 40%);">+"  -b bps       set the TTY to the specified bit rate\n"</span><br><span style="color: hsl(120, 100%, 40%);">+"  -e escape    set the escape character (default: ~)\n"</span><br><span style="color: hsl(120, 100%, 40%);">+"  -l logfile   log all output to the specified file\n"</span><br><span style="color: hsl(120, 100%, 40%);">+"  -t delay_ms  wait the specified amount of time between input characters\n"</span><br><span style="color: hsl(120, 100%, 40%);">+"  -T           add timestamps to the log file\n"</span><br><span style="color: hsl(120, 100%, 40%);">+      , name);</span><br><span style="color: hsl(120, 100%, 40%);">+    exit(1);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int main(int argc, char *const *argv)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    char *end;</span><br><span style="color: hsl(120, 100%, 40%);">+    int c, bps;</span><br><span style="color: hsl(120, 100%, 40%);">+    int fd = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    int append = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+    const char *logfile = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+    int throttle_us = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+    int throttle = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    while ((c = getopt(argc, argv, "ab:e:l:t:T")) != EOF)</span><br><span style="color: hsl(120, 100%, 40%);">+  switch (c) {</span><br><span style="color: hsl(120, 100%, 40%);">+      case 'a':</span><br><span style="color: hsl(120, 100%, 40%);">+         append = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+           break;</span><br><span style="color: hsl(120, 100%, 40%);">+            case 'b':</span><br><span style="color: hsl(120, 100%, 40%);">+         bps = strtoul(optarg, &end, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+           if (*end)</span><br><span style="color: hsl(120, 100%, 40%);">+                 usage(*argv);</span><br><span style="color: hsl(120, 100%, 40%);">+             speed = bps_to_speed(bps);</span><br><span style="color: hsl(120, 100%, 40%);">+            break;</span><br><span style="color: hsl(120, 100%, 40%);">+            case 'e':</span><br><span style="color: hsl(120, 100%, 40%);">+         if (strlen(optarg) != 1)</span><br><span style="color: hsl(120, 100%, 40%);">+                  usage(*argv);</span><br><span style="color: hsl(120, 100%, 40%);">+             escape = *optarg;</span><br><span style="color: hsl(120, 100%, 40%);">+             break;</span><br><span style="color: hsl(120, 100%, 40%);">+            case 'l':</span><br><span style="color: hsl(120, 100%, 40%);">+         logfile = optarg;</span><br><span style="color: hsl(120, 100%, 40%);">+             break;</span><br><span style="color: hsl(120, 100%, 40%);">+            case 't':</span><br><span style="color: hsl(120, 100%, 40%);">+         throttle_us = strtoul(optarg, &end, 0)*1000;</span><br><span style="color: hsl(120, 100%, 40%);">+              if (*end)</span><br><span style="color: hsl(120, 100%, 40%);">+                 usage(*argv);</span><br><span style="color: hsl(120, 100%, 40%);">+             break;</span><br><span style="color: hsl(120, 100%, 40%);">+            case 'T':</span><br><span style="color: hsl(120, 100%, 40%);">+         timestamp = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+                break;</span><br><span style="color: hsl(120, 100%, 40%);">+            default:</span><br><span style="color: hsl(120, 100%, 40%);">+          usage(*argv);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+    num_ttys = argc-optind;</span><br><span style="color: hsl(120, 100%, 40%);">+    ttys = argv+optind;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (logfile) {</span><br><span style="color: hsl(120, 100%, 40%);">+ log = fopen(logfile, append ? "a" : "w");</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!log) {</span><br><span style="color: hsl(120, 100%, 40%);">+       perror(logfile);</span><br><span style="color: hsl(120, 100%, 40%);">+      exit(1);</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+     setlinebuf(log);</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    make_raw(0, &console);</span><br><span style="color: hsl(120, 100%, 40%);">+    atexit(cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+    while (1) {</span><br><span style="color: hsl(120, 100%, 40%);">+      struct timeval tv;</span><br><span style="color: hsl(120, 100%, 40%);">+    fd_set set;</span><br><span style="color: hsl(120, 100%, 40%);">+   int res;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (fd < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+          fd = open_next_tty();</span><br><span style="color: hsl(120, 100%, 40%);">+         if (fd > 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+          char buf[1024]; /* enough :-) */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+            sprintf(buf, "\r\r[Open %s]\r\n", ttys[curr_tty]);</span><br><span style="color: hsl(120, 100%, 40%);">+          write_string(buf);</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+     FD_ZERO(&set);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!throttle)</span><br><span style="color: hsl(120, 100%, 40%);">+            FD_SET(0, &set);</span><br><span style="color: hsl(120, 100%, 40%);">+      if (fd >= 0)</span><br><span style="color: hsl(120, 100%, 40%);">+           FD_SET(fd, &set);</span><br><span style="color: hsl(120, 100%, 40%);">+     tv.tv_sec = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+        tv.tv_usec = throttle ? throttle_us : 100000;</span><br><span style="color: hsl(120, 100%, 40%);">+ res = select(fd < 0 ? 1 : fd+1, &set, NULL, NULL, &tv);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (res < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+         perror("select");</span><br><span style="color: hsl(120, 100%, 40%);">+           return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!res)</span><br><span style="color: hsl(120, 100%, 40%);">+         throttle = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     if (FD_ISSET(0, &set)) {</span><br><span style="color: hsl(120, 100%, 40%);">+      if (throttle_us)</span><br><span style="color: hsl(120, 100%, 40%);">+          throttle = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!copy(0, fd, 1, throttle_us != 0))</span><br><span style="color: hsl(120, 100%, 40%);">+            goto failed;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+     if (fd >= 0 && FD_ISSET(fd, &set))</span><br><span style="color: hsl(120, 100%, 40%);">+         if (!copy(fd, 1, 0, 0))</span><br><span style="color: hsl(120, 100%, 40%);">+           goto failed;</span><br><span style="color: hsl(120, 100%, 40%);">+  continue;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+failed:</span><br><span style="color: hsl(120, 100%, 40%);">+  write_string("\r\n[Closed]\r\n");</span><br><span style="color: hsl(120, 100%, 40%);">+   (void) close(fd);</span><br><span style="color: hsl(120, 100%, 40%);">+     fd = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+    return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/libosmocore/+/17242">change 17242</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.osmocom.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.osmocom.org/c/libosmocore/+/17242"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: libosmocore </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: I4f4f0dae6774447eb370fc1488a4689b1d5d54f8 </div>
<div style="display:none"> Gerrit-Change-Number: 17242 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: laforge <laforge@osmocom.org> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>