
Show HN: A library to add a command promp (and telnet) to your programs - buserror
https://github.com/buserror/libmish
======
j1elo
Given the explanation from the Security section (i.e. " _libmish is really
made for developement purpose_ "), it might make more sense that telnet access
is _disabled_ by default and only enabled with something like _MISH_ON=1_.

Otherwise, this becomes yet another thing to disable upon deployment, chances
are people will forget they used this during development and production code
will get deployed without the variable to disable telnet.

------
buserror
This is a "C19 lockdown project" \-- I always want that feature in long
running processes: quick way to do introspection in a program without having
to create a whole subsystem for it. Also, a way to 'connect' back in,
especially for programs that don't log anything.

Demo is in C, but it shouldnt be too hard to add bindings for other languages.

This is very fresh paint, it had a demo of "one" in one of my other program,
so I do expect the odd issue!

~~~
ggerganov
Nice little project! I did something similar [0], but instead of telnet, the
lib opens a websocket and you connect with your browser.

[0] -
[https://github.com/ggerganov/incppect](https://github.com/ggerganov/incppect)

------
skissane
> 'telnet' on linux doesn't handle UNIX sockets

Someone should write a patch to fix that. There is no reason why the telnet
protocol can't be used over UNIX domain sockets.

As you note, telnet has some advantages over socat/netcat/etc, e.g. the
Negotiate About Window Size (NAWS) option can be used to notify terminal
resize events (SIGWINCH).

One could also register and implement a telnet authentication type [1] to
trigger authentication via SCM_CREDENTIALS. The server could use this to
confirm the client has the expected UID/GID.

[1] [https://www.iana.org/assignments/telnet-options/telnet-
optio...](https://www.iana.org/assignments/telnet-options/telnet-
options.xhtml#telnet-options-4)

~~~
buserror
Good idea, altho I will have to check if busybox telnet for example supports
it.

~~~
skissane
busybox telnet and busybox telnetd both support Unix domain sockets. As in
this example:

sudo busybox telnetd -b local:/telnetd.sock

busybox telnet local:/telnetd.sock

Then you can log in over telnet over Unix domain socket.

HOWEVER, this only works if busybox is built with CONFIG_FEATURE_UNIX_LOCAL=y.
And normally, busybox isn't built with that enabled. It is one of the few
features which "make defconfig" doesn't enable.

I wonder if busybox devs could be convinced to change defconfig to set
CONFIG_FEATURE_UNIX_LOCAL=y? Their FAQ [1] says defconfig "enables all
functionality except special purpose things like selinux or debugging support
which would reduce the portability of the resulting binary". I don't think
AF_UNIX/AF_LOCAL is really a "special purpose thing".
(CONFIG_FEATURE_UNIX_LOCAL turns on AF_UNIX support for all relevant busybox
networking commands, it isn't telnet specific.)

Of course, their telnet client doesn't support my SCM_CREDENTIALS as a telnet
auth option idea either. The busybox code is reasonably clean so it wouldn't
be hard to add it to their telnet client, even their telnetd just for testing
(can't say the same for netkit-telnet, which is default telnet client/server
on Debian/Ubuntu). Not sure if the busybox developers would be willing to
accept such a random feature though.

PS: telnet/telnetd in Alpine Linux are compiled with
CONFIG_FEATURE_UNIX_LOCAL=y. However, the default busybox in Alpine 3.7 and
above doesn't include telnet/telnetd. You can install them though with "apk
add busybox-extras"

PPS: on macOS, Homebrew's telnet also supports telnet to Unix domain sockets,
e.g. telnet /tmp/telnetd.sock.

[1]
[https://www.busybox.net/FAQ.html#configure](https://www.busybox.net/FAQ.html#configure)

------
p4bl0
The first thing I thought of is "I could use this to telnet into long running
computation processes and force them to flush those buffers to see where
they're at and how it goes". This use case alone make this kind of thing
worthy :).

~~~
dspillett
Some tools respond to a Unix signal
([https://en.wikipedia.org/wiki/Signal_(IPC)](https://en.wikipedia.org/wiki/Signal_\(IPC\)))
this way, usually SIGUSR1 or SIGUSR2 though I've seen SIGCONT used too. SIGINT
also, though that feels wrong to me as it circumvents the commonly expected
response to ctrl-c.

~~~
p4bl0
Yes that's actually what I did for my last long running jobs: I trapped
SIGUSR1 so that when my process receives it it flushes its buffers.

------
dpenguin
Cool! You could provide a read line-based command line interface with server
or client driven completions to make it even more ready to use.

~~~
jddj
Along this line, there are some open source MUD clients around which are
(naturally) entirely telnet focused and embed lua, regex, aliases and usually
their own extensible UI frameworks.

------
eqvinox
For comparison, FRRouting has 25 year old CLI code:

[https://github.com/FRRouting/frr/blob/master/doc/developer/c...](https://github.com/FRRouting/frr/blob/master/doc/developer/cli.rst)

[https://github.com/FRRouting/frr/blob/master/lib/command.c](https://github.com/FRRouting/frr/blob/master/lib/command.c)
(+ other files nearby)

looks like this:

    
    
      DEFUN (show_version,
             show_version_cmd,
             "show version",
             SHOW_STR
             "Displays zebra version\n")
      { ... }
      
      install_element(VIEW_NODE, &show_version_cmd);
    

self-registering commands (__attribute__((constructor))) are on the TODO list.

On the other hand we have automatic parameter parsing into proper C types
(look for "DEFPY" in the code/docs.)

disclaimer: I conceived and implemented DEFPY.

------
fomojola
Nice: typically you can also embed a web interface that is only available on
localhost, but the telnet-style interface can be handy too.

------
tn1
If you want to access this remotely and telnet by itself won't do, remember
that setting up SSH tunneling is very easy!

~~~
neverartful
Good suggestion! Insecure protocols routed through secure protocols are always
an option. Tip for those who do this in production - add sufficient
documentation/training so that the new guy on the team knows about it.

------
jonahbenton
Nice- obligatory mention that this is old hat in languages with REPLs. In
Clojure, shipping to production with a REPL port (running over an HTTPS
websocket, only accessible from a bastion host) is common practice and saves a
lot of work making ad hoc admin tools and troubleshooting from logs.

------
mishoo
Now add a Common Lisp compiler to it and you reinvented technology from the
80'es. :-)

~~~
hnlmorg
If you want to get pedantic then clear text consoles are the core concept
behind time sharing systems, the methodology behind Multics and UNIX, and thus
pre-dates LISP Machines by decades (we all stand on the shoulders of giants).

But that doesn't mean there isn't still value in someone writing a new library
and sharing it with the community.

~~~
lispm
Interactive LISP consoles were used before UNIX or Multics existed.

~~~
hnlmorg
Indeed however to be analogous with the submission those consoles would still
need to be running on a time sharing system like I described.

Time sharing systems also pre-date UNIX and Multics, which is why I exampled
them as being operating systems that use the time sharing metaphor.

~~~
lispm
Right, Time sharing wasn't implemented yet at that time, when Lisp was having
its first interactive console in 1960. It was then also implemented on the
first time sharing operating systems.

~~~
hnlmorg
ok, let's put it another way, a LISP console is all well and good but systems
in 1960 weren't yet multi-tasking (or at least multi-tasking systems in 1960
were still rare) so you couldn't have a LISP process running as one thread and
query it's running state from another.

The reason I keep hammering on about time sharing is because they largely came
hand in hand, time sharing systems needed to support multi-tasking whereas
single user systems didn't (you still see echoes of this in the 80s with CP/M,
DOS and 8-bit micros running BASIC). In fact if you look at the history of
multi-tasking it roughly follows the same time line and lineage as the uptake
of time sharing systems (around mid '60s IIRC). Before then you'd have to stop
the execution of one program before you could start execution on another.

I know some "LISPians" like to think everything in computing eventually leads
back to LISP but that's not always the case (and I say that as a big fan of
the language myself).

~~~
lispm
One might just write a main loop, which takes Lisp expressions (or other
input) from two or more different terminals and executes each expression
interleaved. Those were not 'concurrent programs' which shared some state, but
function calls within the same Lisp system, using an interactive execution
loop serving several I/O devices. An early (mid 60s) application domain would
be multi-player games over terminals controlled from a single process.

Lisp also has the idea of break loops, which halt the current execution (for
example triggered by some kind of interrupt), allow interaction with the
program state and then let one continue the program in some way. Thus one
would not need to attach a debugger I/O loop from another process, but the
debugger repl would be a part of the running program and could be called on
demand.

That you 'know some "LISPians" like to think everything in computing
eventually leads back to LISP but that's not always the case' doesn't
invalidate the fact that Lisp systems were running on many of the early
computers & operating systems from 1960 onwards and followed their evolution.
Thus at least some interesting stuff has been done very early in Lisp, too.

~~~
hnlmorg
> _One might just write a main loop, which takes Lisp expressions (or other
> input) from two or more different terminals and executes each expression
> interleaved._

Pre-time sharing systems weren't multi-terminal. Hence why I keep coming back
to time sharing.

Also your main loop example could be done in assembly, FORTRAN, BASIC, Pascal
and C, two of which also pre-date LISP. There's nothing uniquely LISP about
writing a polling loop.

Job control et al and multi-terminal mainframes are something very much born
out of time sharing. You could use LISP to write your application that would
run atop of that time sharing system if you wanted but you could also write
that same application in a bunch of other languages too (assuming you had a
compiler for that machine). And bare in mind around this time you could still
physically inspect the state of paused program on a fair amount of single
process systems (and those you couldn't would often punch out verbose log of
its running state to tape).

> _An early (mid 60s) application domain_

By the mid 60s you had time sharing systems. Your argument was that LISP was
doing this before then.

> _That you 'know some "LISPians" like to think everything in computing
> eventually leads back to LISP but that's not always the case' doesn't
> invalidate the fact that Lisp systems were running on many of the early
> computers & operating systems from 1960 onwards and followed their
> evolution. Thus at least some interesting stuff has been done very early in
> Lisp, too._

I completely agree but you're attributing credit to LISP for something that
isn't a language-specific feature. At least the OP was referencing a computing
platform.

Anyway, this whole conversation was ridiculous from the outset and a massive
distraction to the submission that sparked it. It doesn't really matter what
came first; the only reason I even commented was to illustrate that we're all
standing on the shoulders of giants so it's pointless mocking a submission for
being similar in design to tech that pre-dates it. It's ironic that post lead
to an argument about what came first.

~~~
lispm
> main loop example could be done in assembly

'Could' is the word. My example was that it was actually done to have built-in
command loops and the building blocks for those (so that they could be used in
programs), which could be invoked on demand while a program was running.

> it's pointless mocking a submission for being similar in design

I'm not mocking the submission. The feature is quite valuable and interacting
with running software via command loops is great.

~~~
hnlmorg
> _' Could' is the word._

Ok, "was" then. Operating systems were originally written in assembly and you
can't write an operating system without some kind of hardware polling and
command loops (even in the days before multi-tasking systems).

Even earlier computers in the days before operating systems would have command
loops written in giant rings of punched sheets that would slowly spin round on
reels like a cambelt. So this isn't even an innovation that was born from
assembly, let alone any high level language.

I honestly do get what you're saying and I'm not trying to dismiss your point
that people did this kind of stuff in LISP but what you need to understand is
that people did this in a great number of different ways, in different
languages and even mechanically too.

------
neverartful
I have an existing use case for this. One of the music players that I use a
lot is command line based and I start it to run with shuffle-play (i.e., play
songs randomly). Sometimes the song chosen isn't something I want to listen to
at that time. However, I don't have a nice way of skipping/advancing the
currently playing song. If I really want to skip the song I have to Ctrl-C out
of it and restart it. I was going to add signal handlers for SIGUSR1, but I
didn't know how to do this in Python.

Adding a library such as this one, I could also enhance to alter queued up
songs.

~~~
ComodoHacker
Try mpd or VLC with telnet interface.

~~~
neverartful
Thanks for mpd suggestion. I had not heard of it before and it appears to be
exactly what I want.

------
msravi
I write automated trading algos for my own trading, and invariably put in a
"command line interface" so I can control variables during trading without
having to restart. I don't know if this is the normal thing to do, but I just
read the variables from a mysql table at some logical point in the program
loop, where changing it is "safe". The CLI is then simply about connecting to
the DB and changing those variables using standard SQL statements... I guess
the "right" way would be to build a proper (G)UI to handle this, but hey, it
works.

~~~
tyingq
In the Java world, it's very common to use JMX for that, and there are several
JMX clients to read/set variables, watch logs, etc.

~~~
neverartful
I too thought of JMX when reading through comments. I've always thought that
JMX was underrated.

------
cheez
I feel like this should be simpler:

    
    
      MISH_CMD_NAMES(set, "set");
      MISH_CMD_HELP(set,
        "set 'cnt' variable",
        "test command for libmish!");
      MISH_CMD_REGISTER(set, _test_set_cnt);
    

Should be

    
    
      MISH_CMD(set,
               _test_set_cnt,
               "set 'cnt' variable",
               "test command for libmish!")
    

Which can expand as needed.

~~~
buserror
Yes, I like the single macro, but that doesn't handle the multiple possible
names, and multiple lines of help... but I agree, I probably will add a single
alias for simple cases.

------
jhallenworld
__attribute__((constructor,used))

This is very clever.. in my CLI I use a dedicated section to build a command
table, but the linker script often has to be modified for this to work. The
above avoids the need to change the linker script at all (but you do need an
init function, which I don't..). It means you have to compile as C++, right?

~~~
lunixbochs
constructor works fine in C as with GCC/clang. It links down to
.init/.init_array on ELF (though all the linker cares about is PT_DYNAMIC and
DT_INIT / DT_INIT_ARRAY)

------
ktpsns
For all the people who complain about telnet: If it would use Unix domain
sockets, the whole system would rebuild what you can do with ordinary
stdin/stdout and GNU screen or tmux. I think the network transparency is a
feature of this library.

~~~
aargh_aargh
Can you please elaborate?

~~~
ktpsns
GNU Screen and tmux are popular terminal multiplexers. One of the central
features of such command line programs are to detach sessions from the actual
user terminal emulators. The multiplexer is then running in the background,
collecting stdout/stderr of the running shells and programs. When the user
reconnects (this is typically not network transparent, but there are
"extensions" such as [https://tmate.io/](https://tmate.io/)), she can
immediately continue to interact with the programs.

------
VadimPR
You could then automate tasks in your program with a scriptable telnet client
([https://github.com/mudlet/mudlet](https://github.com/mudlet/mudlet))

(disclosure, I'm on the dev team)

------
amelius
Be aware that random websites can access localhost through your browser.

~~~
sadfklsjlkjwt
Can they? I thought this recent issue was related only to websockets?

------
Quarrel
Very nice.

Now just need a small wrapper around ptrace to inject the library and call
mish_prepare(0), so can just dynamically add it as needed to running programs.

------
andybak
Should the title not specify "to your own C programs" or similar?

Or if it is more widely applicable then how would it work with non-C programs?

------
brockrockman
I feel like this hits Tcl's use case pretty well, too. Old but not forgotten.

------
antoineMoPa
Can't complain about the security; there is no security.

------
jbverschoor
Oh my..

So we have COM, WCF, AppleScript/OSA, heck, we used to use two connections
with FTP. One data, one control.

Nevertheless. it's nice if it's easily implementable.

~~~
michaelmcmillan
I thought we still used two connections with FTP?

~~~
buserror
Not in PASV mode, which is the only mode supported these days.

~~~
0x0
PASV is still two connections, it just swaps around who's connecting to who.

------
TedDoesntTalk
Nice job, but do you know about Prometheus?
[https://prometheus.io/](https://prometheus.io/)

~~~
derision
prometheus is completely different

