From: Steinar H. Gunderson Date: Tue, 2 Jan 2024 17:47:33 +0000 (+0100) Subject: Create $(libdir) on make install. X-Git-Url: https://git.sesse.net/?p=cubemap;a=commitdiff_plain;h=HEAD;hp=ccad164a0a43a9a026af76d61b35068e7b5d3ab8;ds=sidebyside Create $(libdir) on make install. --- diff --git a/Makefile.in b/Makefile.in index b4d8456..c910c25 100644 --- a/Makefile.in +++ b/Makefile.in @@ -5,12 +5,18 @@ PROTOC=protoc CPPFLAGS=@CPPFLAGS@ CPPFLAGS += -Itlse -DWITH_KTLS -DNO_TLS_LEGACY_SUPPORT -DNO_SSL_COMPATIBLE_INTERFACE -DLTM_DESC -DTLS_REEXPORTABLE -DNO_TLS_WITH_CHACHA20_POLY1305 CXXFLAGS=-Wall @CXXFLAGS@ @protobuf_CFLAGS@ @libsystemd_CFLAGS@ @libtomcrypt_CFLAGS@ -pthread +CFLAGS=-Wall @CFLAGS@ LDFLAGS=@LDFLAGS@ -pthread LIBS=@LIBS@ @protobuf_LIBS@ @libsystemd_LIBS@ @libtomcrypt_LIBS@ +SYSTEMDSYSTEMUNITDIR=@SYSTEMDSYSTEMUNITDIR@ OBJS=main.o client.o server.o stream.o udpstream.o serverpool.o input.o input_stats.o httpinput.o udpinput.o parse.o config.o acceptor.o stats.o accesslog.o thread.o util.o log.o metacube2.o sa_compare.o timespec.o state.pb.o tlse/tlse.o +ifeq (@have_ffmpeg@,yes) +all: cubemap ffmpeg_metacube_hack.so +else all: cubemap +endif %.pb.cc %.pb.h : %.proto $(PROTOC) --cpp_out=. $< @@ -23,42 +29,49 @@ all: cubemap $(CXX) -MMD -MP $(CPPFLAGS) $(CXXFLAGS) -o $@ -c $< cubemap: $(OBJS) $(CXX) -o cubemap $(OBJS) $(LIBS) $(LDFLAGS) +ffmpeg_metacube_hack.so: ffmpeg_metacube_hack.c + $(CC) -shared $(CPPFLAGS) $(CFLAGS) -fPIC -o $@ $< -ldl DEPS=$(OBJS:.o=.d) -include $(DEPS) clean: - $(RM) cubemap $(OBJS) $(DEPS) state.pb.h state.pb.cc + $(RM) cubemap $(OBJS) $(DEPS) state.pb.h state.pb.cc ffmpeg_metacube_hack.so -PREFIX=@prefix@ -SYSCONFDIR=@sysconfdir@ -LOCALSTATEDIR=@localstatedir@ +prefix=@prefix@ +sysconfdir=@sysconfdir@ +localstatedir=@localstatedir@ +libdir=@libdir@ install: $(INSTALL) -m 755 -o root -g root -d \ - $(DESTDIR)$(PREFIX)/bin \ - $(DESTDIR)$(PREFIX)/share/man/man1 \ - $(DESTDIR)$(SYSCONFDIR) \ - $(DESTDIR)$(LOCALSTATEDIR)/lib/cubemap \ - $(DESTDIR)$(LOCALSTATEDIR)/log/cubemap \ - $(DESTDIR)$(PREFIX)/share/munin/plugins \ - $(DESTDIR)/lib/systemd/system - $(INSTALL) -m 755 -o root -g root cubemap $(DESTDIR)$(PREFIX)/bin/cubemap - $(INSTALL) -m 755 -o root -g root munin/cubemap munin/cubemap_input $(DESTDIR)$(PREFIX)/share/munin/plugins/ - gzip -c cubemap.1 > $(DESTDIR)$(PREFIX)/share/man/man1/cubemap.1.gz + $(DESTDIR)$(prefix)/bin \ + $(DESTDIR)$(prefix)/share/man/man1 \ + $(DESTDIR)$(sysconfdir) \ + $(DESTDIR)$(localstatedir)/lib/cubemap \ + $(DESTDIR)$(localstatedir)/log/cubemap \ + $(DESTDIR)$(prefix)/share/munin/plugins \ + $(DESTDIR)$(SYSTEMDSYSTEMUNITDIR) + $(INSTALL) -m 755 -o root -g root cubemap $(DESTDIR)$(prefix)/bin/cubemap + $(INSTALL) -m 755 -o root -g root munin/cubemap munin/cubemap_input $(DESTDIR)$(prefix)/share/munin/plugins/ +ifeq (@have_ffmpeg@,yes) + $(INSTALL) -m 755 -o root -g root -d $(DESTDIR)$(libdir) + $(INSTALL) -m 755 -o root -g root ffmpeg_metacube_hack.so $(DESTDIR)$(libdir)/ +endif + gzip -c cubemap.1 > $(DESTDIR)$(prefix)/share/man/man1/cubemap.1.gz sed \ - -e "s,@prefix[@],$(PREFIX),g" \ - -e "s,@sysconfdir[@],$(SYSCONFDIR),g" \ - cubemap.service.in > $(DESTDIR)/lib/systemd/system/cubemap.service + -e "s,@prefix[@],$(prefix),g" \ + -e "s,@sysconfdir[@],$(sysconfdir),g" \ + cubemap.service.in > $(DESTDIR)$(SYSTEMDSYSTEMUNITDIR)/cubemap.service # Make sure we don't overwrite an existing configuration. - test ! -f $(DESTDIR)$(SYSCONFDIR)/cubemap.config + test ! -f $(DESTDIR)$(sysconfdir)/cubemap.config sed \ - -e "s,cubemap\.stats,$(LOCALSTATEDIR)/lib/cubemap/\0,g" \ - -e "s,cubemap-input\.stats,$(LOCALSTATEDIR)/lib/cubemap/\0,g" \ - -e "s,access\.log,$(LOCALSTATEDIR)/log/cubemap/\0,g" \ - -e "s,cubemap\.log,$(LOCALSTATEDIR)/log/cubemap/\0,g" \ + -e "s,cubemap\.stats,$(localstatedir)/lib/cubemap/\0,g" \ + -e "s,cubemap-input\.stats,$(localstatedir)/lib/cubemap/\0,g" \ + -e "s,access\.log,$(localstatedir)/log/cubemap/\0,g" \ + -e "s,cubemap\.log,$(localstatedir)/log/cubemap/\0,g" \ -e 's,^stream,#\0,g' \ -e 's,^udpstream,#\0,g' \ - cubemap.config.sample > $(DESTDIR)$(SYSCONFDIR)/cubemap.config + cubemap.config.sample > $(DESTDIR)$(sysconfdir)/cubemap.config .PHONY: clean install .SUFFIXES: diff --git a/NEWS b/NEWS index a154c1e..10d5c0e 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,30 @@ +Cubemap 1.5.1, 2023-12-17 + + * Use systemd.pc to place systemd system units. + Patch by Chris Hofstaedtler. + + +Cubemap 1.5.0, 2023-12-09 + + * Support input from pipes (subprocesses). + + With a program that can output Metacube on stdout, such as a suitably + patched FFmpeg binary, this gives Cubemap transcoding/remuxing capabilities. + + Of course, one cannot upgrade such a binary by SIGHUP-ing it, but it will + survive a Cubemap reload/restart. + + * Add a LD_PRELOAD-able library to force Metacube output from FFmpeg. + + This hooks just the right amount of functions to add Metacube output + to arbitrary FFmpeg programs, but is obviously very brittle. + (Native FFmpeg support would be better, but a patch did not + go through when I tried a while back.) It is only lightly tested. + Documentation in the README and cubemap.config.sample. + + * Various bugfixes. + + Cubemap 1.4.3, 2019-11-17 * Keep the HLS backlog even if the stream header changes. Useful @@ -6,7 +33,7 @@ Cubemap 1.4.3, 2019-11-17 discontinuities). * Various bugfixes. - + Cubemap 1.4.2, 2018-12-21 diff --git a/README b/README index e9520d3..46a943a 100644 --- a/README +++ b/README @@ -1,5 +1,4 @@ -Cubemap is a high-performance, high-availability video reflector, -specifically made for use with VLC. +Cubemap is a high-performance, high-availability video reflector. A short list of features: @@ -11,10 +10,12 @@ A short list of features: and sending a SIGHUP; all clients will continue as if nothing had happened (unless you delete the stream they are watching, of course). Cubemap also survives the encoder dying and reconnecting. - - Support for setting max pacing rate through the fq packet scheduler - (depends on Linux 3.13 or newer). + - Support for setting max pacing rate through the fq packet scheduler. - Reflects anything VLC can reflect over HTTP, even the muxes VLC - has problems reflecting itself (in particular, FLV). + has problems reflecting itself (in particular, FLV). Can also + call out to external programs to receive or fetch streams in arbitrary + ways (e.g. through SRT), although of course they do not necessarily + have the same intrinsic high-availability reconfiguration support. - Multicast support, both for sending and receiving (supports only protocols that can go over UDP, e.g. MPEG-TS). Supports both ASM and SSM. - TLS output support, through the TLSe library (requires libtomcrypt) @@ -27,12 +28,23 @@ A short list of features: HOWTO: - sudo apt install libprotobuf-dev protobuf-compiler libsystemd-dev libtomcrypt-dev + sudo apt install libprotobuf-dev protobuf-compiler libsystemd-dev libtomcrypt-dev pkg-config autoconf + sudo apt install libavformat-dev libavutil-dev # Optional + autoreconf -I . ./configure make -j4 -If you want to use HTTP input (you probably want to), you want VLC 2.2.0 -or newer. Then start the VLC encoder with the “metacube” flag to the http +Cubemap does not in itself understand the format of video streams; +it requires HTTP input that is already delineated into headers and blocks, +so that it knows what parts of the stream to skip to clients that +connect after the stream has been. (The exception is self-synchronizing +streams with no headers, such as MPEG-TS. These can also be sent or +received over UDP) The input format is specific to Cubemap and is called +Metacube (technically, Metacube2, but Metacube1 is long gone and nothing +uses it anymore). Currently, you have three options to create Metacube +video streams: VLC, Nageru or FFmpeg with a hack. + +For VLC, start the VLC encoder with the “metacube” flag to the http access mux, like this: cvlc [...] --sout '#std{access=http{metacube,mime=video/x-flv},mux=flv,dst=:4013/test.flv}' @@ -40,8 +52,18 @@ access mux, like this: Then look through cubemap.config.sample, copy it to cubemap.config, compile and start cubemap. -Nageru, my free video mixer, can also produce Metacube streams natively. -See the manual at https://nageru.sesse.net/doc/ for more information. +Nageru, my free video mixer, can also produce Metacube streams natively, +and so can its included transcoder/remuxer Kaeru. See the manual +at https://nageru.sesse.net/doc/ for more information. + +If you feel very adventurous, you can use LD_PRELOAD to load +ffmpeg_metacube_hack.so into an FFmpeg-using binary. (This is +experimental; native Metacube support is vastly preferred.) +For instance, here's one way you can use the ffmpeg(1) binary +to serve your webcam to Cubemap: + + LD_PRELOAD=ffmpeg_metacube_hack.so ffmpeg -i /dev/video0 -f mpegts -listen 1 'http://[::]:9095' + To upgrade cubemap (after you've compiled a new binary), or to pick up new config: diff --git a/acceptor.cpp b/acceptor.cpp index 31901dd..75dd70b 100644 --- a/acceptor.cpp +++ b/acceptor.cpp @@ -104,7 +104,7 @@ Acceptor::Acceptor(const AcceptorProto &serialized) { // Set back the close-on-exec flag for the socket. // (This can't leak into a child, since we haven't been started yet.) - fcntl(server_sock, F_SETFD, O_CLOEXEC); + fcntl(server_sock, F_SETFD, FD_CLOEXEC); } AcceptorProto Acceptor::serialize() const diff --git a/client.cpp b/client.cpp index 5d5c179..fb278e2 100644 --- a/client.cpp +++ b/client.cpp @@ -72,7 +72,7 @@ Client::Client(const ClientProto &serialized, const vectorpacing_rate, sizeof(stream->pacing_rate)) == -1) { diff --git a/configure.ac b/configure.ac index 0e51aed..1d9bc39 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ AC_CONFIG_MACRO_DIR([m4]) -AC_INIT(cubemap, 1.4.3) +AC_INIT(cubemap, 1.5.2-pre) AC_CONFIG_SRCDIR(main.cpp) @@ -9,6 +9,15 @@ PKG_PROG_PKG_CONFIG PKG_CHECK_MODULES([libsystemd], [libsystemd]) PKG_CHECK_MODULES([protobuf], [protobuf]) PKG_CHECK_MODULES([libtomcrypt], [libtomcrypt]) +PKG_CHECK_VAR(SYSTEMDSYSTEMUNITDIR, systemd, systemdsystemunitdir) + +have_ffmpeg=yes +PKG_CHECK_MODULES([libavformat], [libavformat], [true], [have_ffmpeg=no]) +PKG_CHECK_MODULES([libavutil], [libavutil], [true], [have_ffmpeg=no]) +AC_SUBST([have_ffmpeg]) + +AS_IF([test "x$have_ffmpeg" = "xno" ], + [AC_MSG_WARN([FFmpeg libraries not found. ffmpeg_metacube_hack.so will not be built.])]) CXXFLAGS="$CXXFLAGS -std=gnu++11" diff --git a/cubemap.config.sample b/cubemap.config.sample index 51c46d3..967059c 100644 --- a/cubemap.config.sample +++ b/cubemap.config.sample @@ -81,6 +81,20 @@ stream /test.ts src=http://gruessi.zrh.sesse.net:4013/test.ts src_encoding=raw # to be some reasonable fraction of your fragment length. stream /stream.mp4 src=http://gruessi.zrh.sesse.net:9095/test.mp4.metacube hls_playlist=/stream.m3u8 hls_frag_duration=6 backlog_size=20971520 hls_backlog_margin=1048576 allow_origin=* +# An example of using pipe: to read from a program that generates Metacube +# on standard output (or you can use src_encoding=raw for MPEG_TS). +# This shows how to use FFmpeg, combined with a LD_PRELOAD hack +# (the .so file is built with Cubemap if you have the FFmpeg development +# libraries instealled), to receive MPEG-TS over SRT and remux it into MP4. +# Note that using ffmpeg_metacube_hack.so is experimental and not recommended +# if you can avoid it. +# +# Only one instance of the command will be started at a time (it will drive a +# stream just like an HTTP input will). Such subprocesses will survive +# a Cubemap reload, but can of course themselves not be live-reloaded +# like Cubemap can. Standard error will be passed through unchanged. +stream /srt.mp4 src=pipe:"LD_PRELOAD=ffmpeg_metacube_hack.so ffmpeg -loglevel warning -i 'srt://[::]:9710?mode=listener' -c:v copy -c:a copy -f mp4 -movflags empty_moov+frag_keyframe+default_base_moof+skip_trailer -frag_duration 125000 -bsf:a aac_adtstoasc -" + # UDP input. TS is the most common container to use over UDP (you cannot # take any arbitrary container and expect it to work). # backlog_size= overrides the backlog, which is normally 10 MB. diff --git a/ffmpeg_metacube_hack.c b/ffmpeg_metacube_hack.c new file mode 100644 index 0000000..4398cd6 --- /dev/null +++ b/ffmpeg_metacube_hack.c @@ -0,0 +1,356 @@ +// A shared library that you can LD_PRELOAD into an FFmpeg-using process +// (most likely ffmpeg(1)) to make it output Metacube. This is obviously +// pretty hacky, since it needs to override various FFmpeg functions, +// so there are few guarantees here. It is written in C to avoid pulling +// in the C++ runtime into FFmpeg's C-only world. +// +// You should not link to this library. It does not have ABI stability. +// It is licensed the same as the rest of Cubemap. + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "metacube2.h" + +static pthread_once_t metacube2_crc_once_control = PTHREAD_ONCE_INIT; +static AVCRC metacube2_crc_table[257]; + +// We need to store some extra information for each context, +// so this is where we do it. The “opaque” field in the AVIOContext +// points to this struct, but we can also look it up by the AVIOContext +// pointer by scanning through the singly linked list starting with +// first_extra_data. +struct ContextExtraData { + struct ContextExtraData *next; // NULL for last entry. + AVIOContext *ctx; // The context we are associating data with. + bool seen_sync_point; + void *old_opaque; + int (*old_write_data_type)(void *opaque, uint8_t * buf, + int buf_size, + enum AVIODataMarkerType type, + int64_t time); + + // Used during avformat_write_header(), to combine adjacent header blocks + // into one (in particular, the MP4 mux has an unneeded avio_flush() + // halfway throughout). + bool in_header; + int64_t header_first_time; + uint8_t *buffered_header; + size_t buffered_header_bytes; +}; +static struct ContextExtraData *first_extra_data = NULL; + +// Look up ContextExtraData for the given context, creating a new one if needed. +static struct ContextExtraData *get_extra_data(AVIOContext * ctx) +{ + for (struct ContextExtraData * ed = first_extra_data; ed != NULL; + ed = ed->next) { + if (ed->ctx == ctx) { + return ed; + } + } + struct ContextExtraData *ed = (struct ContextExtraData *) + malloc(sizeof(struct ContextExtraData)); + ed->ctx = ctx; + ed->seen_sync_point = false; + ed->old_write_data_type = NULL; + ed->in_header = false; + ed->buffered_header = NULL; + ed->buffered_header_bytes = 0; + + ed->next = first_extra_data; + first_extra_data = ed; + return ed; +} + +// Clear ContextExtraData for a given context (presumably before it's freed). +static void free_extra_data(AVIOContext * ctx) +{ + if (first_extra_data == NULL) { + return; + } + if (first_extra_data->ctx == ctx) { + struct ContextExtraData *to_free = first_extra_data; + first_extra_data = to_free->next; + free(to_free); + return; + } + for (struct ContextExtraData * ed = first_extra_data; ed != NULL; + ed = ed->next) { + if (ed->next != NULL && ed->next->ctx == ctx) { + struct ContextExtraData *to_free = ed->next; + ed->next = to_free->next; + free(to_free); + return; + } + } +} + +static void metacube2_crc_init_table_once(void) +{ + av_assert0(av_crc_init + (metacube2_crc_table, 0, 16, 0x8fdb, + sizeof(metacube2_crc_table)) >= 0); +} + +static uint16_t metacube2_compute_crc_ff(const struct + metacube2_block_header *hdr) +{ + static const int data_len = sizeof(hdr->size) + sizeof(hdr->flags); + const uint8_t *data = (uint8_t *) & hdr->size; + uint16_t crc; + + pthread_once(&metacube2_crc_once_control, + metacube2_crc_init_table_once); + + // Metacube2 specifies a CRC start of 0x1234, but its pycrc-derived CRC + // includes a finalization step that is done somewhat differently in av_crc(). + // 0x1234 alone sent through that finalization becomes 0x394a, and then we + // need a byte-swap of the CRC value (both on input and output) to account for + // differing conventions. + crc = av_crc(metacube2_crc_table, 0x4a39, data, data_len); + return av_bswap16(crc); +} + +static int write_packet(void *opaque, uint8_t * buf, int buf_size, + enum AVIODataMarkerType type, int64_t time) +{ + if (buf_size < 0) { + return AVERROR(EINVAL); + } + + struct ContextExtraData *ed = (struct ContextExtraData *) opaque; + + if (ed->in_header) { + if (ed->buffered_header_bytes == 0) { + ed->header_first_time = time; + } + + size_t new_buffered_header_bytes = + ed->buffered_header_bytes + buf_size; + if (new_buffered_header_bytes < ed->buffered_header_bytes) { + return AVERROR(ENOMEM); + } + ed->buffered_header = + (uint8_t *) realloc(ed->buffered_header, + new_buffered_header_bytes); + if (ed->buffered_header == NULL) { + return AVERROR(ENOMEM); + } + + memcpy(ed->buffered_header + ed->buffered_header_bytes, + buf, buf_size); + ed->buffered_header_bytes = new_buffered_header_bytes; + return buf_size; + } + // Find block size if we add a Metacube2 header in front. + unsigned new_buf_size = + (unsigned) buf_size + sizeof(struct metacube2_block_header); + if (new_buf_size < (unsigned) buf_size + || new_buf_size > (unsigned) INT_MAX) { + // Overflow. + return -1; + } + // Fill in the header. + struct metacube2_block_header hdr; + int flags = 0; + if (type == AVIO_DATA_MARKER_SYNC_POINT) + ed->seen_sync_point = 1; + else if (type == AVIO_DATA_MARKER_HEADER) + // NOTE: If there are multiple blocks marked METACUBE_FLAGS_HEADER, + // only the last one will count. This may become a problem if the + // mux flushes halfway through the stream header; if so, we would + // need to keep track of and concatenate the different parts. + flags |= METACUBE_FLAGS_HEADER; + else if (ed->seen_sync_point) + flags |= METACUBE_FLAGS_NOT_SUITABLE_FOR_STREAM_START; + + memcpy(hdr.sync, METACUBE2_SYNC, sizeof(hdr.sync)); + AV_WB32(&hdr.size, buf_size); + AV_WB16(&hdr.flags, flags); + AV_WB16(&hdr.csum, metacube2_compute_crc_ff(&hdr)); + + int ret; + ed->ctx->opaque = ed->old_opaque; + if (new_buf_size < ed->ctx->max_packet_size) { + // Combine the two packets. (This is what we normally want.) + // So we allocate a new block, with a Metacube2 header in front. + uint8_t *buf_with_hdr = (uint8_t *) malloc(new_buf_size); + if (buf_with_hdr == NULL) { + return AVERROR(ENOMEM); + } + memcpy(buf_with_hdr, &hdr, sizeof(hdr)); + memcpy(buf_with_hdr + sizeof(hdr), buf, buf_size); + if (ed->old_write_data_type) { + ret = + ed->old_write_data_type(ed->old_opaque, + buf_with_hdr, + new_buf_size, type, + time); + } else { + ret = + ed->ctx->write_packet(ed->old_opaque, + buf_with_hdr, + new_buf_size); + } + free(buf_with_hdr); + + if (ret >= 0 + && ret >= sizeof(struct metacube2_block_header)) { + ret -= sizeof(struct metacube2_block_header); + } + } else { + // Send separately. This will split a header block if it's really large, + // which we don't want, but that's how things are. + if (ed->old_write_data_type) { + ret = + ed->old_write_data_type(ed->old_opaque, + (uint8_t *) & hdr, + sizeof(hdr), type, + time); + } else { + ret = + ed->ctx->write_packet(ed->old_opaque, + (uint8_t *) & hdr, + sizeof(hdr)); + } + if (ret < 0) { + return ret; + } + if (ret != sizeof(hdr)) { + return AVERROR(EIO); + } + + if (ed->old_write_data_type) { + ret = + ed->old_write_data_type(ed->old_opaque, buf, + buf_size, type, time); + } else { + ret = + ed->ctx->write_packet(ed->old_opaque, buf, + buf_size); + } + } + + ed->ctx->opaque = ed; + return ret; +} + +// Actual hooked functions below. + +int avformat_write_header(AVFormatContext * ctx, AVDictionary ** options) +{ + metacube2_crc_init_table_once(); + + struct ContextExtraData *ed = get_extra_data(ctx->pb); + ed->old_opaque = ctx->pb->opaque; + ed->old_write_data_type = ctx->pb->write_data_type; + ctx->pb->opaque = ed; + ctx->pb->write_data_type = write_packet; + ctx->pb->seek = NULL; + ctx->pb->seekable = 0; + if (ed->old_write_data_type == NULL) { + ctx->pb->ignore_boundary_point = 1; + } + + int (*original_func)(AVFormatContext * ctx, + AVDictionary ** options); + original_func = dlsym(RTLD_NEXT, "avformat_write_header"); + + ed->in_header = true; + int ret = (*original_func) (ctx, options); + ed->in_header = false; + + if (ed->buffered_header_bytes > 0) { + int hdr_ret = write_packet(ed, ed->buffered_header, + ed->buffered_header_bytes, + AVIO_DATA_MARKER_HEADER, + ed->header_first_time); + free(ed->buffered_header); + ed->buffered_header = NULL; + + if (hdr_ret >= 0 && hdr_ret < ed->buffered_header_bytes) { + hdr_ret = AVERROR(EIO); + } + ed->buffered_header_bytes = 0; + if (hdr_ret < 0) { + return hdr_ret; + } + } + + return ret; +} + +void avformat_free_context(AVFormatContext * ctx) +{ + if (ctx == NULL) { + return; + } + free_extra_data(ctx->pb); + + void (*original_func)(AVFormatContext * ctx); + original_func = dlsym(RTLD_NEXT, "avformat_free_context"); + return (*original_func) (ctx); +} + +// Hook so that we can restore opaque instead of ours being freed by the caller. +int avio_close(AVIOContext * ctx) +{ + if (ctx == NULL) { + return 0; + } + struct ContextExtraData ed = *get_extra_data(ctx); + free_extra_data(ctx); + ctx->opaque = ed.old_opaque; + + int (*original_func)(AVIOContext * ctx); + original_func = dlsym(RTLD_NEXT, "avio_close"); + return (*original_func) (ctx); +} + +// Identical to FFmpeg's definition, but we cannot hook avio_close() +// when called from FFmpeg's avio_closep(), so we need to hook this one +// as well. +int avio_closep(AVIOContext ** s) +{ + int ret = avio_close(*s); + *s = NULL; + return ret; +} + + +int avio_open2(AVIOContext ** s, const char *filename, int flags, + const AVIOInterruptCB * int_cb, AVDictionary ** options) +{ + // The options, if any, are destroyed on entry, so we can add new ones + // pretty freely. + if (options && *options) { + AVDictionaryEntry *listen = + av_dict_get(*options, "listen", NULL, + AV_DICT_MATCH_CASE); + if (listen != NULL && atoi(listen->value) != 0) { + // If -listen is set, we'll want to add a header, too. + av_dict_set(options, "headers", + "Content-encoding: metacube\r\n", + AV_DICT_APPEND); + } + } + + int (*original_func)(AVIOContext ** s, const char *filename, + int flags, const AVIOInterruptCB * int_cb, + AVDictionary ** options); + original_func = dlsym(RTLD_NEXT, "avio_open2"); + return (*original_func) (s, filename, flags, int_cb, options); +} diff --git a/httpinput.cpp b/httpinput.cpp index 62ed663..b2b6207 100644 --- a/httpinput.cpp +++ b/httpinput.cpp @@ -80,7 +80,9 @@ HTTPInput::HTTPInput(const InputProto &serialized) { // Set back the close-on-exec flag for the socket. // (This can't leak into a child, since we haven't been started yet.) - fcntl(sock, F_SETFD, O_CLOEXEC); + if (sock != -1) { + fcntl(sock, F_SETFD, FD_CLOEXEC); + } pending_data.resize(serialized.pending_data().size()); memcpy(&pending_data[0], serialized.pending_data().data(), serialized.pending_data().size()); @@ -167,6 +169,7 @@ int HTTPInput::lookup_and_connect(const string &host, const string &port) log(WARNING, "[%s] Lookup of '%s' failed (%s).", url.c_str(), host.c_str(), gai_strerror(err)); } + freeaddrinfo(ai); return -1; } @@ -198,6 +201,7 @@ int HTTPInput::lookup_and_connect(const string &host, const string &port) bool complete = wait_for_activity(sock, POLLIN | POLLOUT, nullptr); if (should_stop()) { safe_close(sock); + freeaddrinfo(base_ai); return -1; } if (complete) { diff --git a/httpinput.h b/httpinput.h index 1ee1f8f..0c3b534 100644 --- a/httpinput.h +++ b/httpinput.h @@ -100,7 +100,7 @@ private: // The socket we are downloading on (or -1). int sock = -1; - // pid of the cihld process (or -1). + // pid of the child process (or -1). pid_t child_pid = -1; // Mutex protecting . diff --git a/main.cpp b/main.cpp index 18976fc..50fd48e 100644 --- a/main.cpp +++ b/main.cpp @@ -623,6 +623,9 @@ start: } for (const auto &key_and_input_with_refcount : inputs) { key_and_input_with_refcount.second.input->stop(); + if (stopped) { + key_and_input_with_refcount.second.input->close_socket(); + } } servers->stop(); diff --git a/server.cpp b/server.cpp index a6f4574..8025fd1 100644 --- a/server.cpp +++ b/server.cpp @@ -70,6 +70,11 @@ Server::Server() Server::~Server() { safe_close(epoll_fd); + + // We're going to die soon anyway, but clean this up to keep leak checking happy. + for (const auto &acceptor_and_context : tls_server_contexts) { + tls_destroy_context(acceptor_and_context.second); + } } vector Server::get_client_stats() const diff --git a/serverpool.cpp b/serverpool.cpp index c7ff10c..7b790c2 100644 --- a/serverpool.cpp +++ b/serverpool.cpp @@ -181,10 +181,11 @@ void ServerPool::add_data(int stream_index, const char *data, size_t bytes, uint void ServerPool::set_unavailable(int stream_index) { - assert(stream_index >= 0 && stream_index < ssize_t(num_http_streams)); - - for (int i = 0; i < num_servers; ++i) { - servers[i].set_unavailable(stream_index); + assert(stream_index >= 0 && stream_index < ssize_t(num_http_streams + udp_streams.size())); + if (stream_index < ssize_t(num_http_streams)) { + for (int i = 0; i < num_servers; ++i) { + servers[i].set_unavailable(stream_index); + } } } diff --git a/stream.cpp b/stream.cpp index 4e5ca24..c79391a 100644 --- a/stream.cpp +++ b/stream.cpp @@ -69,7 +69,7 @@ Stream::Stream(const StreamProto &serialized, int data_fd) } // Set the close-on-exec parameter back on the backlog fd. - fcntl(data_fd, F_SETFD, O_CLOEXEC); + fcntl(data_fd, F_SETFD, FD_CLOEXEC); for (ssize_t point : serialized.suitable_starting_point()) { if (point == -1) { diff --git a/tlse/README b/tlse/README index 3fa5ca5..8e91f40 100644 --- a/tlse/README +++ b/tlse/README @@ -2,7 +2,6 @@ This is a copy of the core files of TLSe, checked out from https://github.com/eduardsui/tlse -It is patched to add and include ktls.h, since glibc does not include -kTLS definitions yet, and then irrelevant files removed. It is embedded -(as opposed to a regular link) since TLSe does not provide a static or -shared library. +It has had irrelevant files removed, and a dummy ktls.h is added to +forward to glibc's implementation. It is embedded (as opposed to +a regular link) since TLSe does not provide a static or shared library. diff --git a/tlse/ktls.h b/tlse/ktls.h index 7ed59c4..4693d2c 100644 --- a/tlse/ktls.h +++ b/tlse/ktls.h @@ -1,80 +1 @@ -/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-2-Clause) */ -/* - * Copyright (c) 2016-2017, Mellanox Technologies. All rights reserved. - * - * This software is available to you under a choice of one of two - * licenses. You may choose to be licensed under the terms of the GNU - * General Public License (GPL) Version 2, available from the file - * COPYING in the main directory of this source tree, or the - * OpenIB.org BSD license below: - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * - Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. - * - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _UAPI_LINUX_TLS_H -#define _UAPI_LINUX_TLS_H - -#include - -/* TLS socket options */ -#define TLS_TX 1 /* Set transmit parameters */ -#define TLS_RX 2 /* Set receive parameters */ - -/* Supported versions */ -#define TLS_VERSION_MINOR(ver) ((ver) & 0xFF) -#define TLS_VERSION_MAJOR(ver) (((ver) >> 8) & 0xFF) - -#define TLS_VERSION_NUMBER(id) ((((id##_VERSION_MAJOR) & 0xFF) << 8) | \ - ((id##_VERSION_MINOR) & 0xFF)) - -#define TLS_1_2_VERSION_MAJOR 0x3 -#define TLS_1_2_VERSION_MINOR 0x3 -#define TLS_1_2_VERSION TLS_VERSION_NUMBER(TLS_1_2) - -/* Supported ciphers */ -#define TLS_CIPHER_AES_GCM_128 51 -#define TLS_CIPHER_AES_GCM_128_IV_SIZE 8 -#define TLS_CIPHER_AES_GCM_128_KEY_SIZE 16 -#define TLS_CIPHER_AES_GCM_128_SALT_SIZE 4 -#define TLS_CIPHER_AES_GCM_128_TAG_SIZE 16 -#define TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE 8 - -#define TLS_SET_RECORD_TYPE 1 - -struct tls_crypto_info { - __u16 version; - __u16 cipher_type; -}; - -struct tls12_crypto_info_aes_gcm_128 { - struct tls_crypto_info info; - unsigned char iv[TLS_CIPHER_AES_GCM_128_IV_SIZE]; - unsigned char key[TLS_CIPHER_AES_GCM_128_KEY_SIZE]; - unsigned char salt[TLS_CIPHER_AES_GCM_128_SALT_SIZE]; - unsigned char rec_seq[TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE]; -}; - -#define SOL_TLS 282 -#define TCP_ULP 31 - -#endif /* _UAPI_LINUX_TLS_H */ +#include diff --git a/udpinput.cpp b/udpinput.cpp index 873b0b9..824c3bc 100644 --- a/udpinput.cpp +++ b/udpinput.cpp @@ -123,7 +123,7 @@ UDPInput::UDPInput(const InputProto &serialized) { // Set back the close-on-exec flag for the socket. // (This can't leak into a child, since we haven't been started yet.) - fcntl(sock, F_SETFD, O_CLOEXEC); + fcntl(sock, F_SETFD, FD_CLOEXEC); // Should be verified by the caller. string protocol; diff --git a/version.h b/version.h index 4a2a26f..fe89b31 100644 --- a/version.h +++ b/version.h @@ -1,9 +1,7 @@ #ifndef _VERSION_H #define _VERSION_H -// Version number. Don't expect this to change all that often. - -#define SERVER_VERSION "1.4.4-pre" +#define SERVER_VERSION "1.5.2-pre" #define SERVER_IDENTIFICATION "Cubemap/" SERVER_VERSION #endif // !defined(_VERSION_H)