|
modemu2k 0.2.4
Library that provides telnet capability to a comm program
|
GitHub repository |
Public API for the modemu2k PTY-based modem emulator library. More...
#include <poll.h>#include <stddef.h>#include "modemu2k_version.h"Go to the source code of this file.
Macros | |
| #define | M2K_API |
| Public-symbol visibility/export marker. | |
| #define | M2K_DEPRECATED(msg) |
| #define | M2K_ERROR_BUFFER_SIZE 256 |
| #define | M2K_MAX_POLLFDS 3 |
Typedefs | |
| typedef struct m2k_s | m2k_t |
| Opaque modem emulator context. Create with m2k_new(), destroy with m2k_free(). | |
| typedef void(* | m2k_log_fn) (const char *msg, void *userdata) |
| Log callback type. | |
Enumerations | |
| enum | m2k_log_level_t { M2K_LOG_ERROR = 0 , M2K_LOG_WARN , M2K_LOG_INFO , M2K_LOG_DEBUG , M2K_LOG_TRACE } |
| Log severity levels, ordered from most to least severe. More... | |
| enum | m2k_err_t { M2K_OK = 0 , M2K_ERR_NOMEM , M2K_ERR_PTY , M2K_ERR_SOCKET , M2K_ERR_TIMEOUT , M2K_ERR_CANCELED , M2K_ERR_BUG , M2K_ERR_WOULDBLOCK , M2K_ERR_AT } |
| Return codes used by all m2k_* functions. More... | |
Functions | |
| M2K_API const char * | m2k_version (void) |
| Runtime version string of the linked libmodemu2k. | |
| M2K_API m2k_t * | m2k_new (void) |
| Allocate and initialise a new modem context. | |
| M2K_API void | m2k_free (m2k_t *ctx) |
Release all resources held by ctx. | |
| M2K_API void | m2k_set_log_fn (m2k_t *ctx, m2k_log_fn fn, void *userdata) |
| Install a log callback. | |
| M2K_API void | m2k_set_error_buffer (m2k_t *ctx, char *buf, size_t size) |
| Install a buffer that receives a detailed message for the most recent error. | |
| M2K_API m2k_err_t | m2k_atcmd (m2k_t *ctx, const char *cmd) |
| Feed a Hayes AT command string to the modem. | |
| M2K_API | M2K_DEPRECATED ("use m2k_run() or the step API instead") m2k_err_t m2k_dial(m2k_t *ctx |
Open a TCP connection to host : port. | |
| M2K_API m2k_err_t | m2k_hangup (m2k_t *ctx) |
| Hang up: abandon whatever connection activity is in progress. | |
| M2K_API m2k_err_t | m2k_escape (m2k_t *ctx) |
| Request an immediate return to command mode from online mode. | |
| M2K_API const char * | m2k_strerror (m2k_err_t err) |
Return a human-readable string for err. | |
| M2K_API m2k_err_t | m2k_setup_stdin (m2k_t *ctx) |
| Use stdin/stdout as the TTY (standalone mode). | |
| M2K_API m2k_err_t | m2k_setup_pty (m2k_t *ctx, const char **slave_out) |
| Allocate a PTY master and return the slave device path. | |
| M2K_API m2k_err_t | m2k_setup_comm_program (m2k_t *ctx, const char *cmd) |
| Allocate a PTY and fork/exec a comm program on the slave. | |
| M2K_API m2k_err_t | m2k_setup_dev (m2k_t *ctx, const char *dev) |
| Open an existing PTY device as the TTY. | |
| M2K_API m2k_err_t | m2k_setup_listen (m2k_t *ctx, const char *port) |
Bind a TCP listening socket on port. | |
| M2K_API m2k_err_t | m2k_setup_app_io (m2k_t *ctx) |
| Embed mode: no real TTY fd; the host application supplies the bytes a TTY would normally produce, via m2k_write_from_app(), and consumes the bytes modemu2k would normally send to the TTY, via m2k_read_to_app(). | |
| M2K_API m2k_err_t | m2k_write_from_app (m2k_t *ctx, const void *buf, size_t len, size_t *consumed) |
| Push bytes into the modem as if they had been read from the TTY. | |
| M2K_API m2k_err_t | m2k_read_to_app (m2k_t *ctx, void *buf, size_t max, size_t *len_out) |
| Drain bytes from the modem that would normally have been written to the TTY. | |
| M2K_API int | m2k_has_pending_output (const m2k_t *ctx) |
| Test whether the modem has TTY-bound bytes still buffered. | |
| M2K_API int | m2k_get_listen_fd (const m2k_t *ctx) |
| Expose the listening socket's fd (after m2k_setup_listen, before m2k_listen_accept). | |
| M2K_API m2k_err_t | m2k_listen_accept (m2k_t *ctx) |
| Accept a single incoming connection on the listening socket opened by m2k_setup_listen() and adopt it as the TTY. | |
| M2K_API m2k_err_t | m2k_setup_answer (m2k_t *ctx, const char *port) |
Bind the line-side "answer" listener on port. | |
| M2K_API int | m2k_get_answer_fd (const m2k_t *ctx) |
| Expose the answer listener's fd (after m2k_setup_answer()). | |
| M2K_API m2k_err_t | m2k_run (m2k_t *ctx) |
| Run the modem command/online loop until the PTY closes. | |
| M2K_API m2k_err_t | m2k_get_pollfds (m2k_t *ctx, struct pollfd *fds, size_t *nfds_inout, int *timeout_ms) |
| Describe the fds and timeout the caller's event loop should watch. | |
| M2K_API m2k_err_t | m2k_step (m2k_t *ctx, struct pollfd *fds, size_t nfds) |
| Run one non-blocking iteration of the state machine. | |
| M2K_API int | m2k_run_done (const m2k_t *ctx) |
| Test whether the session has ended. | |
| M2K_API int | m2k_is_online (const m2k_t *ctx) |
| Test whether the modem is currently in online mode. | |
| M2K_API int | m2k_describe_state (const m2k_t *ctx, char *buf, size_t cap) |
Write a human-readable snapshot of the modem's state into buf. | |
| M2K_API int | m2k_has_carrier (const m2k_t *ctx) |
| Test whether the modem has an active carrier (live TCP socket). | |
| M2K_API void | m2k_set_dtr (m2k_t *ctx, int on) |
| Set the host's DTR (Data Terminal Ready) signal state. | |
| M2K_API void | m2k_set_rts (m2k_t *ctx, int on) |
| Set the host's RTS (Request to Send) signal state. | |
| M2K_API int | m2k_get_dtr (const m2k_t *ctx) |
| M2K_API int | m2k_get_rts (const m2k_t *ctx) |
| M2K_API void | m2k_set_force_verbose (m2k_t *ctx, int on) |
| Bypass the ATV verbose category mask. | |
| M2K_API int | m2k_get_force_verbose (const m2k_t *ctx) |
| M2K_API void | m2k_set_log_level (m2k_t *ctx, m2k_log_level_t level) |
| Set the severity threshold for log delivery. | |
| M2K_API m2k_log_level_t | m2k_get_log_level (const m2k_t *ctx) |
Variables | |
| M2K_API const char * | host |
| M2K_API const char const char * | port |
Public API for the modemu2k PTY-based modem emulator library.
The pattern used by the bundled modemu2k executable (see src/main.c) shows the recommended way to use the library:
For finer control — driving the state machine yourself rather than letting m2k_run() block — replace m2k_run() with a poll loop built around m2k_get_pollfds() + m2k_step(), exiting when m2k_run_done() returns nonzero. Dialing from code is done by feeding the ATD string through the CMD-mode line buffer (write to the TTY in standalone mode, or m2k_write_from_app() in embed mode); m2k_step() then walks the DIAL → ONLINE transition. m2k_escape() and m2k_hangup() drive the online→cmd and disconnect transitions programmatically.
| #define M2K_API |
Public-symbol visibility/export marker.
On platforms where it matters (Windows DLLs, ELF builds with -fvisibility=hidden) this macro decorates every documented public function below. Defaults to nothing on Unix-default builds, where symbols are visible already. Define M2K_BUILDING_DLL before including this header inside the modemu2k source tree on Windows to flip the dllimport/dllexport direction; consumers leave it undefined.
Borrowed from libuv (UV_EXTERN) / libcurl (CURL_EXTERN) / libssh2 (LIBSSH2_API).
| #define M2K_ERROR_BUFFER_SIZE 256 |
Recommended minimum size for the m2k_set_error_buffer() buffer.
| #define M2K_MAX_POLLFDS 3 |
Maximum number of pollfds modemu2k will ever ask the caller to watch.
| typedef void(* m2k_log_fn) (const char *msg, void *userdata) |
Log callback type.
| msg | NUL-terminated log message. |
| userdata | Caller-supplied pointer passed unchanged from m2k_set_log_fn(). |
| enum m2k_err_t |
Return codes used by all m2k_* functions.
Values are stable across releases: new codes are appended and existing values are never reordered, so callers can safely cache the numbers.
| Enumerator | |
|---|---|
| M2K_OK | Success. |
| M2K_ERR_NOMEM | Memory allocation failed. |
| M2K_ERR_PTY | PTY open or allocation failed. |
| M2K_ERR_SOCKET | TCP connect or I/O error. |
| M2K_ERR_TIMEOUT | Operation timed out. (Reserved; not currently returned — dial timeouts surface as NO CARRIER through the step machine.) |
| M2K_ERR_CANCELED | Operation canceled (e.g., +++ escape sequence). |
| M2K_ERR_BUG | Internal assertion failure — should not happen. |
| M2K_ERR_WOULDBLOCK | Operation would block; call m2k_step() (or wait for the relevant fd to become ready) and retry. Mirrors libssh2's LIBSSH2_ERROR_EAGAIN. |
| M2K_ERR_AT | AT command rejected by the lexer (malformed input). |
| enum m2k_log_level_t |
Log severity levels, ordered from most to least severe.
Every message the library emits carries one of these levels; only messages at or above the threshold set by m2k_set_log_level() reach the log callback (or stderr). DEBUG opens the state-machine and telnet-negotiation narration; TRACE additionally opens byte-level buffer traffic. The Hayes-side ATV mask can still open individual DEBUG/TRACE categories independently of the level.
Feed a Hayes AT command string to the modem.
Intended for one-shot configuration commands (S-register sets, mode toggles, etc.). Action commands like ATD/ATO/ATA are recognised by the lexer but do not drive a connection from this entry point; feed them through the CMD-mode line buffer instead (see
| ctx | Modem context. |
| cmd | NUL-terminated AT command (e.g. "ATZ" or "ATS0=1"). A string with no AT prefix is silently ignored. |
AT prefix); M2K_ERR_AT if the lexer rejected the command as malformed, or if the command was an action verb (ATD, ATO, or ATA) — those drive a connection and so are not actionable from this one-shot configuration entry. To dial, answer, or resume online mode from code, feed the command string through the CMD-mode line buffer (m2k_write_from_app() in embed mode, or just write to the TTY in standalone mode) and let m2k_step() dispatch it. M2K_ERR_BUG only for unexpected lexer states.Open a TCP connection to host : port.
Enter online mode and relay data between the PTY and the socket.
Transitions the modem from idle to connected state. Call m2k_online() after this to start relaying data.
| ctx | Modem context. |
| host | Hostname or numeric IP address. |
| port | Service name or decimal port number. |
Blocks until the connection closes or the +++ escape sequence is detected. Returns M2K_ERR_CANCELED when the caller escapes back to command mode.
| ctx | Modem context (must have an active connection from m2k_dial()). |
Write a human-readable snapshot of the modem's state into buf.
Four newline-separated lines covering the step-machine state, carrier, TTY fds and buffer fill, socket and buffer fill, listeners, ring count, dial progress, control lines, and verbose mask. Intended for bug reports and host-side debug dumps; the exact format may evolve between versions and is not a parsing API.
snprintf contract: returns the length the full description requires (excluding the terminating NUL) regardless of cap; writes at most cap - 1 bytes plus a NUL. cap == 0 (with buf NULL or not) is a legal size probe.
| ctx | Modem context. |
| buf | Destination buffer (may be NULL when cap is 0). |
| cap | Capacity of buf in bytes. |
Request an immediate return to command mode from online mode.
Drives the same transition that the +++ escape sequence would — but without the silence-guard timing or sending the literal +++ bytes through the I/O path. Useful from a host application that embeds modemu2k and wants programmatic control of the mode.
The transition takes effect on the next m2k_step() / m2k_run() iteration. The underlying TCP connection is left intact; use m2k_hangup() afterward if you also want to drop it.
A no-op when the context is not currently in online mode.
| ctx | Modem context. |
Release all resources held by ctx.
| ctx | Context to destroy. Safe to call with NULL. |
Expose the answer listener's fd (after m2k_setup_answer()).
Lets an embedding host poll for the incoming-call event itself (the RING indication also flows through the TTY output).
| ctx | Modem context. |
Return the most recent value passed to m2k_set_dtr() (1 by default).
Return the most recent value passed to m2k_set_force_verbose() (0 by default).
Expose the listening socket's fd (after m2k_setup_listen, before m2k_listen_accept).
Lets a host event loop poll the fd for POLLIN — i.e. detect an incoming TCP connection (the "RING" event in modem terms) so it can call m2k_listen_accept() at the right moment instead of blocking unconditionally on it.
| ctx | Modem context. |
| M2K_API m2k_log_level_t m2k_get_log_level | ( | const m2k_t * | ctx | ) |
Return the current log level threshold (M2K_LOG_INFO by default).
| M2K_API m2k_err_t m2k_get_pollfds | ( | m2k_t * | ctx, |
| struct pollfd * | fds, | ||
| size_t * | nfds_inout, | ||
| int * | timeout_ms ) |
Describe the fds and timeout the caller's event loop should watch.
Fills fds with the fds modemu2k currently wants to monitor and stores the longest acceptable wait in *timeout_ms (-1 = wait indefinitely, 0 = poll immediately). Each pollfd's .fd and .events are set; the caller is responsible for the .revents field after calling poll().
| ctx | Modem context. |
| fds | Caller-provided array of at least M2K_MAX_POLLFDS entries. |
| nfds_inout | In: capacity of fds (must be >= M2K_MAX_POLLFDS). Out: number of entries actually populated. May be zero while the session is still alive (app-IO command mode with no listener: the host's own fds drive progress) — use m2k_run_done() to decide when to exit the loop. |
| timeout_ms | Out: maximum wait in milliseconds before m2k_step() needs to be called again. -1 means no deadline. |
*nfds_inout is too small. Return the most recent value passed to m2k_set_rts() (1 by default).
Hang up: abandon whatever connection activity is in progress.
Covers all three connection phases: aborts a dial in progress (M2K_STATE_DIAL), cancels an ATA/auto-answer wait (M2K_STATE_ANSWER), or tears down a live TCP connection. In each case the modem emits NO CARRIER and returns to command mode. A no-op when idle. The answer listener from m2k_setup_answer() stays bound.
| ctx | Modem context. |
Test whether the modem has an active carrier (live TCP socket).
Maps to the DCD (Data Carrier Detect) signal real serial modems expose. Stays true after a +++ escape back to command mode as long as the TCP connection is still up; goes false on hangup, NO CARRIER, or before the first dial.
| ctx | Modem context. |
Test whether the modem has TTY-bound bytes still buffered.
Returns nonzero whenever a subsequent m2k_read_to_app() would deliver at least one byte without first needing another m2k_step()/poll cycle. See m2k_read_to_app() for the back-pressure pattern this predicate exists to support.
Always returns 0 when the context isn't in app-I/O mode.
| ctx | Modem context. |
Test whether the modem is currently in online mode.
Useful for embed-mode hosts that paint mode-specific UI (status bar, local echo toggle, etc.) and need to know whether m2k is relaying bytes to/from a connected socket vs. consuming AT commands.
| ctx | Modem context. |
Accept a single incoming connection on the listening socket opened by m2k_setup_listen() and adopt it as the TTY.
Blocks until a client connects. The listening socket is closed once a connection is accepted (modemu2k handles one client per session). Call before m2k_run().
| ctx | Modem context (must have an active listener from m2k_setup_listen()). |
Allocate and initialise a new modem context.
Drain bytes from the modem that would normally have been written to the TTY.
Only valid after m2k_setup_app_io(). Copies up to max bytes from the internal TTY write buffer into buf and stores the actual length in *len_out (which may be zero if there is nothing pending).
Skipping this collapses throughput to roughly "your buffer size per poll timeout."
| ctx | Modem context. |
| buf | Destination buffer. Must be non-NULL when max > 0. |
| max | Maximum bytes to copy. May be 0. |
| len_out | Out: number of bytes actually copied (0 means no data). Must be non-NULL. |
Run the modem command/online loop until the PTY closes.
Handles the full state machine: reads Hayes AT commands in command mode, dials on ATD, relays data in online mode, and returns to command mode on +++ escape or disconnection. Call one of the m2k_setup_*() functions before calling m2k_run().
Equivalent (modulo poll-error handling) to:
| ctx | Modem context. |
Test whether the session has ended.
| ctx | Modem context. |
Set the host's DTR (Data Terminal Ready) signal state.
On the 1→0 transition while a connection is live, modemu2k hangs the connection up — matching the typical real-modem AT&D2 default of "DTR drop terminates the call". Drop is otherwise a no-op (no carrier to drop). The DTR &D register is not (yet) lexable from AT command strings, so this function is the sole way to drive DTR-related behavior. Default state is asserted (1).
| ctx | Modem context. |
| on | Non-zero to assert DTR, zero to drop it. |
Install a buffer that receives a detailed message for the most recent error.
When buf is non-NULL, internal error paths write a human-readable, contextual message into buf (NUL-terminated, truncated to fit) in addition to any logging via m2k_set_log_fn(). The caller can then present the message after a function returns a non-OK m2k_err_t — curl-style CURLOPT_ERRORBUFFER semantics.
The buffer is only updated when an error actually occurs; successful calls leave it untouched, so callers should check the function's return code first.
| ctx | Modem context. |
| buf | Caller-owned buffer that outlives the context, or NULL to detach. Recommended size: M2K_ERROR_BUFFER_SIZE. |
| size | Capacity of buf in bytes (including the trailing NUL). Ignored when buf is NULL. Passing size = 0 with a non-NULL buf is equivalent to detaching — the pointer is stored but never written through. |
Bypass the ATV verbose category mask.
Narration sites are normally gated by the category mask the Hayes ATV command sets. ATZ resets that mask to its saved default of 0, so a host that wanted log output for the whole run can be silenced by any user-issued ATZ. This flag is OR'd into the gate, so narration survives regardless of the ATV mask. Default is off. Hosts should prefer m2k_set_log_level(), which the standalone CLI's -v flag now uses; this flag remains as a plain mask bypass.
| ctx | Modem context. |
| on | Non-zero to force narration on, zero to defer to ATV. |
| M2K_API void m2k_set_log_fn | ( | m2k_t * | ctx, |
| m2k_log_fn | fn, | ||
| void * | userdata ) |
Install a log callback.
When fn is non-NULL, all diagnostic messages are routed to it instead of stderr. Pass fn = NULL to restore the default stderr output.
| ctx | Modem context. |
| fn | Callback to receive log messages, or NULL. |
| userdata | Opaque pointer forwarded to every fn invocation. |
| M2K_API void m2k_set_log_level | ( | m2k_t * | ctx, |
| m2k_log_level_t | level ) |
Set the severity threshold for log delivery.
Messages less severe than level (numerically greater m2k_log_level_t values) are dropped before they reach the log callback (or stderr). The default is M2K_LOG_INFO, which matches the library's historical always-on output: errors, warnings, and lifecycle notices flow; DEBUG narration and TRACE byte traffic stay quiet until asked for.
The threshold lives outside the Hayes-visible S/% registers, so an ATZ from the far side cannot reset it — this supersedes m2k_set_force_verbose() as the host-side knob (that flag remains as a plain ATV-mask bypass). The ATV mask itself keeps working: it can open individual DEBUG/TRACE categories even when the level threshold would hide them.
The standalone CLI maps -v to M2K_LOG_DEBUG and -vv to M2K_LOG_TRACE.
| ctx | Modem context. |
| level | New threshold; one of m2k_log_level_t. |
Set the host's RTS (Request to Send) signal state.
Tracked for symmetry with real Hayes modems; modemu2k does not act on RTS state (hardware flow control is meaningless for a TCP-based virtual modem — the kernel's socket buffers and TCP windowing do that job). Default state is asserted (1).
| ctx | Modem context. |
| on | Non-zero to assert RTS, zero to drop it. |
Bind the line-side "answer" listener on port.
Incoming TCP connections on this port are treated as modem calls: while one is pending, command mode emits RING (S-register 1 counts the rings); the ATA command — or S0 auto-answer when S0 > 0 — accepts the call and goes online, exactly as if ATD had connected outward. The listener persists across hangups (a real modem's hangup does not remove the phone line) and is closed by m2k_free().
Not to be confused with m2k_setup_listen(), which accepts a TCP connection to use as the TTY (DTE side).
| ctx | Modem context. |
| port | Service name or decimal port number to listen on. |
Embed mode: no real TTY fd; the host application supplies the bytes a TTY would normally produce, via m2k_write_from_app(), and consumes the bytes modemu2k would normally send to the TTY, via m2k_read_to_app().
Intended for embedding modemu2k inside a host program that already owns the terminal/serial interface (e.g. a comm program linking libmodemu2k instead of forking the standalone binary). The socket side still uses real fds and integrates with the host's event loop via m2k_get_pollfds() as usual.
| ctx | Modem context. |
Allocate a PTY and fork/exec a comm program on the slave.
The slave device path is substituted for s in cmd. Call before m2k_run().
| ctx | Modem context. |
| cmd | Shell command; s is replaced by the slave device path. |
Open an existing PTY device as the TTY.
| ctx | Modem context. |
| dev | Path to the PTY master device (e.g. "/dev/ptyp0"). |
Bind a TCP listening socket on port.
Binds and listens on all interfaces (dual-stack IPv4/IPv6); returns immediately without waiting for a connection. Pair with m2k_listen_accept() to accept one connection and use it as the TTY, or integrate the listener fd with the caller's own event loop.
Suitable for use as a virtual-modem backend for programs that connect to modemu2k over TCP, such as dosemu2.
| ctx | Modem context. |
| port | Service name or decimal port number to listen on. |
Allocate a PTY master and return the slave device path.
The returned pointer is owned by the context — do NOT free() it. It remains valid until the next m2k_setup_*() call or m2k_free(ctx).
Use this when you need the slave path itself (e.g. to print it with –show). To fork a comm program on the slave, prefer m2k_setup_comm_program().
| ctx | Modem context. |
| slave_out | Out: pointer to a NUL-terminated slave path inside ctx. Must be non-NULL. |
Run one non-blocking iteration of the state machine.
Consumes the events the caller's poll() reported (via the .revents fields of fds) and advances the cmd-mode / online-mode state once. Does no poll() of its own. Safe to call with all .revents == 0 (the call is then a no-op).
| ctx | Modem context. |
| fds | The pollfd array previously filled by m2k_get_pollfds(), with the OS's .revents values populated by the caller. |
| nfds | Number of entries in fds (the value m2k_get_pollfds returned in *nfds_inout). |
Return a human-readable string for err.
| err | Error code. |
| M2K_API const char * m2k_version | ( | void | ) |
Runtime version string of the linked libmodemu2k.
Lets a caller detect a header/library version mismatch by comparing M2K_VERSION (the value at the time the caller's translation unit was compiled) against m2k_version() (the value the linked library was built with).
| M2K_API m2k_err_t m2k_write_from_app | ( | m2k_t * | ctx, |
| const void * | buf, | ||
| size_t | len, | ||
| size_t * | consumed ) |
Push bytes into the modem as if they had been read from the TTY.
Only valid after m2k_setup_app_io(). Accepts as many bytes from buf as the internal TTY read buffer has room for (partial accept; the actual count is returned via *consumed). The accepted bytes are processed by the next m2k_step() call.
If the buffer is completely full, *consumed is set to 0 and the function returns M2K_ERR_WOULDBLOCK — call m2k_step() to drain, then retry.
| ctx | Modem context. |
| buf | Bytes to inject. Must be non-NULL when len > 0. |
| len | Length of buf in bytes. May be 0. |
| consumed | Out: bytes actually accepted (0 .. len). Must be non-NULL. |
len == 0 is a no-op returning M2K_OK), M2K_ERR_WOULDBLOCK if none were, M2K_ERR_PTY (overloaded as "wrong I/O mode") if the context is not in app-I/O mode — call m2k_setup_app_io() first.