Skip to content

Instantly share code, notes, and snippets.

@bmatcuk
Created January 9, 2020 02:48
Show Gist options
  • Save bmatcuk/faf0ad8137c2bd7d533662d3ba58d270 to your computer and use it in GitHub Desktop.
Save bmatcuk/faf0ad8137c2bd7d533662d3ba58d270 to your computer and use it in GitHub Desktop.
Quick and dirty libuv test

Quick and Dirty libuv Test

This code isn't meant to be pretty: it's just a simple implementation of some libuv features. Specifically:

  • raw mode tty;
  • streaming input from tty;
  • writing output to tty (specifically: echoing the input hex-encoded);
  • handling a signal (SIGTERM, ie, kill pid);
  • quitting on ctrl+c
  • properly cleaning up after ourselves.

Building

Build with make. You'll need to have libuv installed with an appropriate pkg-config file.

test: test.c
cc `pkg-config --libs --cflags libuv` -o test test.c
#include <assert.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <uv.h>
uv_loop_t *loop;
uv_tty_t tty;
uv_tty_t tty_out;
uv_signal_t sigterm;
uv_write_t write_req;
uv_buf_t write_buf[] = { { .base = NULL, .len = 0 } };
char *string_to_hex(char *str, size_t len) {
size_t res_len = (len << 1) + 1;
char *res = malloc(res_len);
for (size_t i = 0; i < len; i++) {
snprintf(res + (i << 1), res_len - (i << 1), "%02hhx", str[i]);
}
res[len << 1] = '\0';
return res;
}
void alloc_cb(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) {
buf->base = malloc(suggested_size);
buf->len = suggested_size;
}
void write_cb(uv_write_t *req, int status) {
assert(status == 0);
free(write_buf[0].base);
write_buf[0].len = 0;
}
void stop() {
int stop_ret = uv_read_stop((uv_stream_t*)&tty);
assert(stop_ret == 0);
int sigstop_ret = uv_signal_stop(&sigterm);
assert(sigstop_ret == 0);
}
void read_cb(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) {
bool end = false;
if (nread > 0) {
write_buf[0].base = string_to_hex(buf->base, (size_t)nread);
write_buf[0].len = ((size_t)nread << 1) + 1;
int write_ret = uv_write(&write_req, (uv_stream_t*)&tty_out, write_buf, 1, &write_cb);
assert(write_ret == 0);
for (ssize_t i = 0; i < nread; i++) {
if (buf->base[i] == (0xf & 'c')) {
end = true;
}
}
} else if (nread < 0) {
// error; stop reading
end = true;
}
if (buf->base && buf->len > 0) {
free(buf->base);
}
if (end) {
stop();
}
}
void signal_cb(uv_signal_t *sig, int signum) {
stop();
}
void walk_and_close_cb(uv_handle_t *handle, void *arg) {
if (!uv_is_closing(handle)) {
uv_close(handle, NULL);
}
}
int main() {
loop = uv_default_loop();
int tty_ret = uv_tty_init(loop, &tty, fileno(stdin), 0);
int tty_out_ret = uv_tty_init(loop, &tty_out, fileno(stdout), 0);
int sig_ret = uv_signal_init(loop, &sigterm);
int mode_ret = uv_tty_set_mode(&tty, UV_TTY_MODE_RAW);
assert(tty_ret == 0);
assert(tty_out_ret == 0);
assert(sig_ret == 0);
assert(mode_ret == 0);
int read_ret = uv_read_start((uv_stream_t*)&tty, &alloc_cb, &read_cb);
assert(read_ret == 0);
int sigstart_ret = uv_signal_start(&sigterm, signal_cb, SIGTERM);
assert(sigstart_ret == 0);
int run_ret = uv_run(loop, UV_RUN_DEFAULT);
assert(run_ret == 0);
int reset_ret = uv_tty_reset_mode();
assert(reset_ret == 0);
uv_walk(loop, &walk_and_close_cb, NULL);
run_ret = uv_run(loop, UV_RUN_DEFAULT);
assert(run_ret == 0);
int close_ret = uv_loop_close(loop);
assert(close_ret == 0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment