├── .gitmodules ├── .editorconfig ├── config.h.cmake ├── dist.sh ├── os └── freebsd │ └── msd_lite ├── .github ├── FUNDING.yml └── workflows │ ├── build-ubuntu-latest.yml │ └── build-macos-latest.yml ├── src ├── CMakeLists.txt ├── msd_lite_stat_text.h ├── stream_sys.h ├── msd_lite_stat_text.c ├── msd_lite.c └── stream_sys.c ├── LICENSE ├── readme.md ├── conf └── msd_lite.conf ├── CMakeLists.txt └── msd_lite.project /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "src/liblcb"] 2 | path = src/liblcb 3 | url = https://github.com/rozhuk-im/liblcb.git 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig: https://EditorConfig.org 2 | ### Rozhuk Ivan 2024 3 | ### https://spec.editorconfig.org/ 4 | ### 5 | 6 | 7 | # top-most EditorConfig file 8 | root = true 9 | 10 | 11 | # Unix-style newlines with a newline ending every file 12 | [*] 13 | indent_style = tab 14 | indent_size = 4 15 | tab_width = 8 16 | end_of_line = lf 17 | charset = utf-8 18 | # spelling_language 19 | trim_trailing_whitespace = true 20 | insert_final_newline = true 21 | -------------------------------------------------------------------------------- /config.h.cmake: -------------------------------------------------------------------------------- 1 | #ifndef __CONFIG_H_IN__ 2 | #define __CONFIG_H_IN__ 3 | 4 | 5 | /*--------------------------------------------------------------------*/ 6 | /* Package information. */ 7 | #define PACKAGE @PROJECT_NAME@ 8 | #define VERSION PACKAGE_VERSION 9 | #define PACKAGE_NAME "@PACKAGE_NAME@" 10 | #define PACKAGE_VERSION "@PACKAGE_VERSION@" 11 | #define PACKAGE_URL "@PACKAGE_URL@" 12 | #define PACKAGE_BUGREPORT "@PACKAGE_BUGREPORT@" 13 | #define PACKAGE_STRING "@PACKAGE_STRING@" 14 | #define PACKAGE_DESCRIPTION "@PACKAGE_DESCRIPTION@" 15 | 16 | 17 | #endif /* __CONFIG_H_IN__ */ 18 | -------------------------------------------------------------------------------- /dist.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | TAR=${2:-"tar"} 4 | 5 | if [ $# -lt 1 ] ; then 6 | echo "Usage: dist.sh [tar_command]" 7 | exit 1 8 | fi 9 | 10 | FILE_NAME=$1 11 | PREFIX=`basename $FILE_NAME | sed -e 's/\.tar.*$//'` 12 | 13 | OUT="" 14 | while true ; do 15 | __mktemp=`which mktemp` 16 | if [ F"$__mktemp" != "F" ] ; then 17 | OUT=`$__mktemp /tmp/files-XXXXXXXX` 18 | break 19 | else 20 | OUT="/tmp/files-`strings -7 /dev/urandom | head -1 | sed -e 's/[^[:alnum:]]//g'`" 21 | fi 22 | if [ ! -f "$OUT" ] ; then 23 | break 24 | fi 25 | done 26 | 27 | git ls-files > $OUT 28 | SUBMODULES=`git submodule | cut -d ' ' -f 3` 29 | 30 | for sub in $SUBMODULES ; do 31 | (cd $sub && git ls-files | sed -e "s|^|$sub/|" >> $OUT) 32 | done 33 | 34 | ${TAR} -c --exclude='.[^/]*' --exclude='*.xz' --exclude='*.gz' --no-recursion --transform "s|^|$PREFIX/|" -a -T $OUT -v -f $FILE_NAME 35 | rm -f $OUT 36 | -------------------------------------------------------------------------------- /os/freebsd/msd_lite: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ### Rozhuk Ivan 2011.06 - 2014 3 | ### startup script file for msd_lite 4 | ### 5 | 6 | 7 | # PROVIDE: msd_lite 8 | # REQUIRE: DAEMON 9 | # BEFORE: LOGIN 10 | # KEYWORD: shutdown 11 | 12 | . /etc/rc.subr 13 | 14 | name="msd_lite" 15 | rcvar=`set_rcvar` 16 | 17 | load_rc_config $name 18 | 19 | : ${msd_lite_enable="NO"} 20 | : ${msd_lite_cfgfile="/root/msd_lite/src/msd_lite.conf"} 21 | : ${msd_lite_pidfile="/var/run/msd_lite.pid"} 22 | : ${msd_lite_user="www"} 23 | : ${msd_lite_group="www"} 24 | : ${msd_lite_chroot=""} 25 | : ${msd_lite_chdir=""} 26 | 27 | 28 | 29 | command="/root/msd_lite/src/msd_lite" 30 | command_args="-d -c ${msd_lite_cfgfile} -P ${msd_lite_pidfile}&" 31 | 32 | pidfile="${msd_lite_chroot}${msd_lite_pidfile}" 33 | required_dirs=${msd_lite_chroot} 34 | required_files="${msd_lite_chroot}${command}" 35 | 36 | 37 | 38 | 39 | 40 | 41 | run_rc_command "$1" 42 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [rozhuk-im] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | #patreon: # Replace with a single Patreon username 5 | #open_collective: # Replace with a single Open Collective username 6 | #ko_fi: # Replace with a single Ko-fi username 7 | #tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | #community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | #liberapay: # Replace with a single Liberapay username 10 | #issuehunt: # Replace with a single IssueHunt username 11 | #lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 12 | #polar: # Replace with a single Polar username 13 | buy_me_a_coffee: rojuc # Replace with a single Buy Me a Coffee username 14 | custom: ['https://paypal.me/rojuc'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 15 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | set(MSD_LITE_BIN msd_lite.c 3 | msd_lite_stat_text.c 4 | stream_sys.c 5 | liblcb/src/net/socket.c 6 | liblcb/src/net/socket_address.c 7 | liblcb/src/net/socket_options.c 8 | liblcb/src/net/utils.c 9 | liblcb/src/proto/http.c 10 | liblcb/src/proto/http_server.c 11 | liblcb/src/threadpool/threadpool.c 12 | liblcb/src/threadpool/threadpool_msg_sys.c 13 | liblcb/src/threadpool/threadpool_task.c 14 | liblcb/src/utils/cmd_line_daemon.c 15 | liblcb/src/utils/sys.c 16 | liblcb/src/utils/sys_res_limits_xml.c 17 | liblcb/src/utils/buf_str.c 18 | liblcb/src/utils/info.c 19 | liblcb/src/utils/ring_buffer.c 20 | liblcb/src/utils/xml.c) 21 | 22 | add_executable(msd_lite ${MSD_LITE_BIN}) 23 | target_compile_definitions(msd_lite PRIVATE -DHTTP_SRV_XML_CONFIG) 24 | target_compile_definitions(msd_lite PRIVATE -DSOCKET_XML_CONFIG) 25 | target_compile_definitions(msd_lite PRIVATE -DTHREAD_POOL_SETTINGS_XML) 26 | set_target_properties(msd_lite PROPERTIES LINKER_LANGUAGE C) 27 | target_link_libraries(msd_lite ${CMAKE_REQUIRED_LIBRARIES} ${CMAKE_EXE_LINKER_FLAGS}) 28 | 29 | install(TARGETS msd_lite RUNTIME DESTINATION bin) 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2011 - 2021, Rozhuk Ivan 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /src/msd_lite_stat_text.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2012 - 2021 Rozhuk Ivan 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | * 26 | * Author: Rozhuk Ivan 27 | * 28 | */ 29 | 30 | #ifndef __MSD_STAT_TEXT_H__ 31 | #define __MSD_STAT_TEXT_H__ 32 | 33 | #include "proto/http_server.h" 34 | #include "stream_sys.h" 35 | 36 | 37 | int gen_hub_stat_text_send_async(str_hubs_bckt_p shbskt, http_srv_cli_p cli); 38 | 39 | int gen_stat_text(const char *package_name, const char *package_version, 40 | str_hubs_bckt_p shbskt, info_sysres_p sysres, 41 | uint8_t *sysinfo, size_t sysinfo_size, 42 | uint8_t *syslimits, size_t syslimits_size, 43 | http_srv_cli_p cli); 44 | 45 | 46 | #endif // __MSD_STAT_TEXT_H__ 47 | -------------------------------------------------------------------------------- /.github/workflows/build-ubuntu-latest.yml: -------------------------------------------------------------------------------- 1 | name: build-ubuntu-latest 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 7 | BUILD_TYPE: Release 8 | 9 | 10 | jobs: 11 | build: 12 | # The CMake configure and build commands are platform agnostic and should work equally 13 | # well on Windows or Mac. You can convert this to a matrix build if you need 14 | # cross-platform coverage. 15 | # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix 16 | runs-on: ubuntu-latest 17 | strategy: 18 | matrix: 19 | compiler: [gcc, clang] 20 | include: 21 | - compiler: gcc 22 | cc: gcc 23 | cxx: g++ 24 | - compiler: clang 25 | cc: clang 26 | cxx: clang++ 27 | steps: 28 | - uses: actions/checkout@v2 29 | with: 30 | submodules: 'recursive' 31 | 32 | - name: Install libraries 33 | run: | 34 | sudo apt-get update 35 | sudo apt-get install cmake libcunit1 libcunit1-doc libcunit1-dev 36 | 37 | - name: Create Build Environment 38 | # Some projects don't allow in-source building, so create a separate build directory 39 | # We'll use this as our working directory for all subsequent commands 40 | run: cmake -E make_directory ${{github.workspace}}/build 41 | 42 | - name: Configure CMake 43 | # Use a bash shell so we can use the same syntax for environment variable 44 | # access regardless of the host operating system 45 | shell: bash 46 | working-directory: ${{github.workspace}}/build 47 | # Note the current convention is to use the -S and -B options here to specify source 48 | # and build directories, but this is only available with CMake 3.13 and higher. 49 | # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 50 | run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DENABLE_TESTS=1 51 | 52 | - name: Build 53 | working-directory: ${{github.workspace}}/build 54 | shell: bash 55 | # Execute the build. You can specify a specific target with "--target " 56 | run: cmake --build . --config $BUILD_TYPE -j 16 57 | 58 | - name: Test 59 | working-directory: ${{github.workspace}}/build 60 | shell: bash 61 | # Execute tests defined by the CMake configuration. 62 | # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail 63 | run: ctest -C $BUILD_TYPE -j 16 --output-on-failure 64 | -------------------------------------------------------------------------------- /.github/workflows/build-macos-latest.yml: -------------------------------------------------------------------------------- 1 | name: build-macos-latest 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 7 | BUILD_TYPE: Release 8 | 9 | 10 | jobs: 11 | build: 12 | # The CMake configure and build commands are platform agnostic and should work equally 13 | # well on Windows or Mac. You can convert this to a matrix build if you need 14 | # cross-platform coverage. 15 | # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix 16 | runs-on: macos-latest 17 | strategy: 18 | matrix: 19 | compiler: [gcc, clang] 20 | include: 21 | - compiler: gcc 22 | cc: gcc 23 | cxx: g++ 24 | - compiler: clang 25 | cc: clang 26 | cxx: clang++ 27 | steps: 28 | - uses: actions/checkout@v2 29 | with: 30 | submodules: 'recursive' 31 | 32 | - name: Install libraries 33 | run: | 34 | checkPkgAndInstall() 35 | { 36 | while [ $# -ne 0 ] 37 | do 38 | rm -f '/usr/local/bin/2to3' 39 | if brew ls --versions $1 ; then 40 | brew upgrade $1 41 | else 42 | brew install $1 43 | fi 44 | shift 45 | done 46 | } 47 | checkPkgAndInstall cunit 48 | 49 | - name: Create Build Environment 50 | # Some projects don't allow in-source building, so create a separate build directory 51 | # We'll use this as our working directory for all subsequent commands 52 | run: cmake -E make_directory ${{github.workspace}}/build 53 | 54 | - name: Configure CMake 55 | # Use a bash shell so we can use the same syntax for environment variable 56 | # access regardless of the host operating system 57 | shell: bash 58 | working-directory: ${{github.workspace}}/build 59 | # Note the current convention is to use the -S and -B options here to specify source 60 | # and build directories, but this is only available with CMake 3.13 and higher. 61 | # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 62 | run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DENABLE_TESTS=1 63 | 64 | - name: Build 65 | working-directory: ${{github.workspace}}/build 66 | shell: bash 67 | # Execute the build. You can specify a specific target with "--target " 68 | run: cmake --build . --config $BUILD_TYPE -j 16 69 | 70 | - name: Test 71 | working-directory: ${{github.workspace}}/build 72 | shell: bash 73 | # Execute tests defined by the CMake configuration. 74 | # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail 75 | run: ctest -C $BUILD_TYPE -j 16 --output-on-failure 76 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # msd_lite 2 | 3 | [![Build-macOS-latest Actions Status](https://github.com/rozhuk-im/msd_lite/workflows/build-macos-latest/badge.svg)](https://github.com/rozhuk-im/msd_lite/actions) 4 | [![Build-Ubuntu-latest Actions Status](https://github.com/rozhuk-im/msd_lite/workflows/build-ubuntu-latest/badge.svg)](https://github.com/rozhuk-im/msd_lite/actions) 5 | 6 | 7 | Rozhuk Ivan 2011-2025 8 | 9 | msd_lite - Multi stream daemon lite. 10 | This lightweight version of Multi Stream daemon (msd) 11 | Program for organizing IP TV streaming on the network via HTTP. 12 | 13 | 14 | ## Licence 15 | BSD licence. 16 | Website: http://www.netlab.linkpc.net/wiki/en:software:msd:lite 17 | 18 | 19 | ## Donate 20 | Support the author 21 | * **GitHub Sponsors:** [!["GitHub Sponsors"](https://camo.githubusercontent.com/220b7d46014daa72a2ab6b0fcf4b8bf5c4be7289ad4b02f355d5aa8407eb952c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f2d53706f6e736f722d6661666266633f6c6f676f3d47697448756225323053706f6e736f7273)](https://github.com/sponsors/rozhuk-im)
22 | * **Buy Me A Coffee:** [!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/rojuc)
23 | * **PayPal:** [![PayPal](https://srv-cdn.himpfen.io/badges/paypal/paypal-flat.svg)](https://paypal.me/rojuc)
24 | * **Bitcoin (BTC):** `1AxYyMWek5vhoWWRTWKQpWUqKxyfLarCuz`
25 | 26 | 27 | ## Features 28 | * Open source 29 | * BSD License 30 | * No deadlocks threads during operation 31 | * Receiving only udp-multicast, including rtp streams 32 | * Not available options URL: precache and blocksize 33 | * Zero Copy on Send (ZCoS) is always on 34 | * No polling to send out to clients fUsePollingForSend 35 | * No analyzer MPEG2-TS stream, and “smart” shipping MPEG2-TS header new clients 36 | 37 | 38 | 39 | 40 | ## Compilation and Installation 41 | ``` 42 | sudo apt-get install build-essential git cmake fakeroot 43 | git clone --recursive https://github.com/rozhuk-im/msd_lite.git 44 | cd msd_lite 45 | mkdir build 46 | cd build 47 | cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_VERBOSE_MAKEFILE=true .. 48 | make -j 8 49 | ``` 50 | 51 | 52 | ## Run tests 53 | ``` 54 | mkdir -p build 55 | cd build 56 | cmake -DCMAKE_BUILD_TYPE=Release -DENABLE_TESTS=1 .. 57 | cmake --build . --config Release -j 16 58 | ctest -C Release --output-on-failure -j 16 59 | ``` 60 | 61 | 62 | ## Usage 63 | ``` 64 | msd_lite [-d] [-v] [-c file] 65 | [-p PID file] [-u uid|usr -g gid|grp] 66 | -h usage (this screen) 67 | -d become daemon 68 | -c file config file 69 | -p PID file file name to store PID 70 | -u uid|user change uid 71 | -g gid|group change gid 72 | -v verboce 73 | ``` 74 | 75 | 76 | ## Setup 77 | 78 | ### msd_lite 79 | Copy %%ETCDIR%%/msd_lite.conf.sample to %%ETCDIR%%/msd_lite.conf 80 | then replace lan0 with your network interface name. 81 | Add more sections if needed. 82 | Remove IPv4/IPv6 lines if not needed. 83 | 84 | Add to /etc/rc.conf: 85 | ``` 86 | msd_lite_enable="YES" 87 | ``` 88 | 89 | Run: 90 | ``` 91 | service msd_lite restart 92 | ``` 93 | 94 | -------------------------------------------------------------------------------- /conf/msd_lite.conf: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 16 | 17 | 18 | 19 | 20 | 6 21 | 22 | 23 | 24 | 1 25 | yes 26 | 27 | 28 | 29 | 30 | 31 | 32 |
0.0.0.0:7088
y
33 |
[::]:7088
34 |
35 | 36 | 37 | * 38 | 39 |
40 | 41 | 42 | 43 | 44 | no 45 | no 46 | yes 47 | yes 48 | 4096 49 | 1024 50 | 51 | 512 52 | 64 53 | htcp 54 | 55 | 56 |
Pragma: no-cache
57 |
Content-Type: video/mpeg
58 |
ContentFeatures.DLNA.ORG: DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01700000000000000000000000000000
59 |
TransferMode.DLNA.ORG: Streaming
60 |
61 |
62 |
63 | 64 | 65 | 66 | 67 | 68 | 512 69 | 48 70 | 2 71 | 72 | 73 | vlan777 74 | 0 75 | 76 | 77 | 78 |
79 | 80 | 81 | -------------------------------------------------------------------------------- /src/stream_sys.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2012-2023 Rozhuk Ivan 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | * 26 | * Author: Rozhuk Ivan 27 | * 28 | */ 29 | 30 | 31 | #ifndef __CORE_STREAM_SYS_H__ 32 | #define __CORE_STREAM_SYS_H__ 33 | 34 | 35 | #include 36 | #include 37 | 38 | #include "utils/macro.h" 39 | #include "threadpool/threadpool.h" 40 | #include "utils/io_buf.h" 41 | #include "threadpool/threadpool_task.h" 42 | #include "utils/ring_buffer.h" 43 | 44 | 45 | typedef struct str_hub_s *str_hub_p; 46 | typedef struct str_hubs_bckt_s *str_hubs_bckt_p; 47 | typedef struct str_src_s *str_src_p; 48 | 49 | 50 | 51 | typedef struct str_hub_cli_s { 52 | TAILQ_ENTRY(str_hub_cli_s) next; /* For list. */ 53 | uintptr_t skt; /* socket */ 54 | r_buf_rpos_t rpos; /* Ring buf read pos. */ 55 | time_t conn_time; /* Connection start time. */ 56 | size_t offset; /* For HTTP headers. */ 57 | uint32_t flags; /* Flags. */ 58 | /* HTTP specific data. */ 59 | uint8_t *user_agent; 60 | size_t user_agent_size; 61 | struct sockaddr_storage remonte_addr; /* Client address. */ 62 | struct sockaddr_storage xreal_addr; 63 | } str_hub_cli_t, *str_hub_cli_p; 64 | TAILQ_HEAD(str_hub_cli_head, str_hub_cli_s); 65 | 66 | /* Flags. */ 67 | #define STR_HUB_CLI_STATE_F_RPOS_INITIALIZED (((uint32_t)1) << 0) 68 | #define STR_HUB_CLI_STATE_F_HTTP_HDRS_SENDED (((uint32_t)1) << 8) 69 | /* Limit for User-Agent len. */ 70 | #define STR_HUB_CLI_USER_AGENT_MAX_SIZE 256 71 | 72 | /* 73 | * 1. Client connect via http, create/find mc receiver, ref_count ++; 74 | * 2. Client send http headers, add self to add_cli_list_head and ref_count --; 75 | * 3. Mc receiver move client from add_cli_list_head to cli_list_head and start 76 | * send stream. 77 | */ 78 | 79 | 80 | 81 | typedef struct str_hub_settings_s { 82 | uint32_t flags; 83 | uint32_t skt_snd_buf; /* For receiver clients. */ 84 | /* Client settings and defaults. */ 85 | size_t cc_name_size; 86 | char cc_name[TCP_CA_NAME_MAX];/* tcp congestion control forced for client. */ 87 | /* End Client settings and defaults. */ 88 | size_t ring_buf_size; /* Size of ring buf. */ 89 | size_t precache; 90 | size_t snd_block_min_size; 91 | uint8_t *cust_http_hdrs; 92 | size_t cust_http_hdrs_size; 93 | } str_hub_settings_t, *str_hub_settings_p; 94 | /* Flags. */ 95 | #define STR_HUB_S_F_DROP_SLOW_CLI (((uint32_t)1) << 5) /* Disconnect lagged clients. */ 96 | #define STR_HUB_S_F_SKT_HALFCLOSED (((uint32_t)1) << 10) /* Enable shutdown(SHUT_RD) for clients. */ 97 | #define STR_HUB_S_F_SKT_TCP_NODELAY (((uint32_t)1) << 11) /* Enable TCP_NODELAY for clients. */ 98 | #define STR_HUB_S_F_SKT_TCP_NOPUSH (((uint32_t)1) << 12) /* Enable TCP_NOPUSH for clients. */ 99 | /* Default values. */ 100 | #define STR_HUB_S_DEF_FLAGS (0) 101 | #define STR_HUB_S_DEF_RING_BUF_SIZE (1 * 1024) /* kb */ 102 | #define STR_HUB_S_DEF_PRECAHE (1 * 1024) /* kb */ 103 | #define STR_HUB_S_DEF_SND_BLOCK_MIN_SIZE (64) /* kb */ 104 | #define STR_HUB_S_DEF_SKT_SND_BUF (256) /* kb */ 105 | 106 | 107 | /* Connection info */ 108 | /* UDP source */ 109 | typedef struct str_src_conn_udp_s { 110 | struct sockaddr_storage addr; 111 | } str_src_conn_udp_t, *str_src_conn_udp_p; 112 | 113 | /* Multicast [rtp] source */ 114 | typedef struct str_src_conn_mc_s { 115 | str_src_conn_udp_t udp; 116 | uint32_t if_index; 117 | uint32_t rejoin_time; 118 | } str_src_conn_mc_t, *str_src_conn_mc_p; 119 | #define STR_SRC_CONN_DEF_IFINDEX ((uint32_t)-1) 120 | 121 | typedef union str_src_conn_params_s { 122 | str_src_conn_udp_t udp; 123 | str_src_conn_mc_t mc; 124 | } str_src_conn_params_t, *str_src_conn_params_p; 125 | 126 | typedef struct str_src_settings_s { 127 | uint32_t skt_rcv_buf; /* For receiver. */ 128 | uint32_t skt_rcv_lowat; /* For receiver. */ 129 | uint64_t rcv_timeout; /* No multicast time to self destroy. */ 130 | } str_src_settings_t, *str_src_settings_p; 131 | /* Default values. */ 132 | #define STR_SRC_S_DEF_SKT_RCV_BUF (512) /* kb */ 133 | #define STR_SRC_S_DEF_SKT_RCV_LOWAT (48) /* kb */ 134 | #define STR_SRC_S_DEF_UDP_RCV_TIMEOUT (2) /* s */ 135 | 136 | 137 | /* 138 | * Auto generated channel name: 139 | * /udp/IPv4MC:PORT@IF_NAME 140 | */ 141 | 142 | typedef struct str_hub_s { 143 | TAILQ_ENTRY(str_hub_s) next; 144 | str_hubs_bckt_p shbskt; 145 | uint8_t *name; /* Stream hub unique name. */ 146 | size_t name_size; /* Name size. */ 147 | struct str_hub_cli_head cli_head; /* List with clients. */ 148 | size_t cli_count; /* Count clients. */ 149 | /* For stat */ 150 | /* Baud rate calculation. */ 151 | struct timespec tp_last_recv; /* For baud rate calculation and status. */ 152 | uint64_t received_count; /* Accumulator for baud rate calculation. */ 153 | uint64_t sended_count; /* Accumulator for baud rate calculation. */ 154 | uint64_t baud_rate_in; /* Total rate in (megabit per sec). */ 155 | uint64_t baud_rate_out; /* Total rate out (megabit per sec). */ 156 | uint64_t dropped_count; /* Dropped clients count. */ 157 | /* -- stat */ 158 | tp_task_p tptask; /* Data/Packets receiver. */ 159 | uintptr_t r_buf_fd; /* r_buf shared memory file descriptor */ 160 | r_buf_p r_buf; /* Ring buf, write pos. */ 161 | #ifdef __linux__ /* Linux specific code. */ 162 | size_t r_buf_rcvd; /* Ring buf LOWAT emulator. */ 163 | #endif /* Linux specific code. */ 164 | time_t next_rejoin_time; /* Next time to send leave+join. */ 165 | 166 | tpt_p tpt; /* Thread data for all IO operations. */ 167 | str_src_conn_params_t src_conn_params; /* Point to str_src_conn_XXX */ 168 | } str_hub_t; 169 | TAILQ_HEAD(str_hub_head, str_hub_s); 170 | 171 | 172 | /* Per thread and summary stats. */ 173 | typedef struct str_hubs_stat_s { 174 | size_t str_hub_count; /* Stream hubs count. */ 175 | size_t cli_count; /* Total clients count. */ 176 | uint64_t baud_rate_in; /* Total rate in (megabit per sec). */ 177 | uint64_t baud_rate_out; /* Total rate out (megabit per sec). */ 178 | } str_hubs_stat_t, *str_hubs_stat_p; 179 | 180 | /* Per thread data */ 181 | typedef struct str_hub_thread_data_s { 182 | struct str_hub_head hub_head; /* List with stream hubs per thread. */ 183 | str_hubs_stat_t stat; 184 | } str_hub_thrd_t, *str_hub_thrd_p; 185 | 186 | 187 | typedef struct str_hubs_bckt_s { 188 | tp_p tp; 189 | struct timespec tp_last_tmr; /* For baud rate calculation. */ 190 | struct timespec tp_last_tmr_next; /* For baud rate calculation. */ 191 | tp_udata_t service_tmr; /* Service timer. */ 192 | str_hub_thrd_p thr_data; /* Per thread hubs + stat. */ 193 | size_t base_http_hdrs_size; 194 | uint8_t base_http_hdrs[512]; 195 | str_hub_settings_t hub_params; /* Settings. */ 196 | str_src_settings_t src_params; /* Settings. */ 197 | } str_hubs_bckt_t; 198 | 199 | 200 | void str_hub_settings_def(str_hub_settings_p p_ret); 201 | void str_src_settings_def(str_src_settings_p p_ret); 202 | void str_src_conn_def(str_src_conn_params_p src_conn_params); 203 | 204 | 205 | int str_hubs_bckt_create(tp_p tp, const char *app_ver, 206 | str_hub_settings_p hub_params, str_src_settings_p src_params, 207 | str_hubs_bckt_p *shbskt_ret); 208 | void str_hubs_bckt_destroy(str_hubs_bckt_p shbskt); 209 | 210 | typedef void (*str_hubs_bckt_enum_cb)(tpt_p tpt, str_hub_p str_hub, void *udata); 211 | int str_hubs_bckt_enum(str_hubs_bckt_p shbskt, str_hubs_bckt_enum_cb enum_cb, 212 | void *udata, tpt_msg_done_cb done_cb); 213 | int str_hubs_bckt_stat_summary(str_hubs_bckt_p shbskt, str_hubs_stat_p stat); 214 | 215 | 216 | str_hub_cli_p str_hub_cli_alloc(uintptr_t skt, const char *ua, size_t ua_size); 217 | void str_hub_cli_destroy(str_hub_p str_hub, str_hub_cli_p strh_cli); 218 | 219 | int str_hub_cli_attach(str_hubs_bckt_p shbskt, str_hub_cli_p strh_cli, 220 | uint8_t *hub_name, size_t hub_name_size, 221 | str_src_conn_params_p src_conn_params); 222 | 223 | 224 | 225 | #endif // __CORE_STREAM_SYS_H__ 226 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Cmake configuration file 3 | # 4 | 5 | ############################# INITIAL SECTION ########################## 6 | cmake_minimum_required(VERSION 3.20) 7 | 8 | project(msd_lite C) 9 | 10 | set(PACKAGE_VERSION_MAJOR 1) 11 | set(PACKAGE_VERSION_MINOR 11) 12 | set(PACKAGE_VERSION_PATCH 0) 13 | 14 | set(PACKAGE_NAME "${PROJECT_NAME}") 15 | set(PACKAGE_VERSION "${PACKAGE_VERSION_MAJOR}.${PACKAGE_VERSION_MINOR}.${PACKAGE_VERSION_PATCH}") 16 | set(PACKAGE_URL "https://github.com/rozhuk-im/msd_lite") 17 | set(PACKAGE_BUGREPORT "https://github.com/rozhuk-im/msd_lite") 18 | set(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}") 19 | set(PACKAGE_DESCRIPTION "Multi stream daemon") 20 | set(PACKAGE_TARNAME "${PACKAGE_NAME}-${PACKAGE_VERSION}") 21 | 22 | ############################# OPTIONS SECTION ########################## 23 | 24 | option(ENABLE_TESTS "Enable tests [default: OFF]" OFF) 25 | if (ENABLE_TESTS) 26 | # Enable testing functionality. 27 | enable_testing() 28 | set(ENABLE_LIBLCB_TESTS TRUE) 29 | endif() 30 | 31 | # Now CMAKE_INSTALL_PREFIX is a base prefix for everything. 32 | if (NOT CONFDIR) 33 | set(CONFDIR "${CMAKE_INSTALL_PREFIX}/etc/msd_lite") 34 | endif() 35 | 36 | if (NOT RUNDIR) 37 | set(RUNDIR "/var/run") 38 | endif() 39 | 40 | 41 | ############################# INCLUDE SECTION ########################## 42 | 43 | include(CheckIncludeFiles) 44 | include(CheckFunctionExists) 45 | include(CheckSymbolExists) 46 | include(CheckCCompilerFlag) 47 | 48 | find_library(PTHREAD_LIBRARY pthread) 49 | list(APPEND CMAKE_REQUIRED_LIBRARIES ${PTHREAD_LIBRARY}) 50 | 51 | ############################# MACRO SECTION ############################ 52 | macro(try_c_flag prop flag) 53 | # Try flag once on the C compiler 54 | check_c_compiler_flag("-Werror ${flag}" C_FLAG_${prop}) 55 | if (C_FLAG_${prop}) 56 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${flag}") 57 | endif() 58 | endmacro() 59 | 60 | macro(try_linker_flag prop flag) 61 | # Check with the C compiler 62 | set(CMAKE_REQUIRED_FLAGS ${flag}) 63 | check_c_compiler_flag(${flag} LINKER_FLAG_${prop}) 64 | set(CMAKE_REQUIRED_FLAGS "") 65 | if (LINKER_FLAG_${prop}) 66 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${flag}") 67 | set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${flag}") 68 | set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${flag}") 69 | endif() 70 | endmacro() 71 | 72 | function(install_if_not_exists src dest destname suffix) 73 | if (NOT IS_ABSOLUTE "${src}") 74 | set(src "${CMAKE_CURRENT_SOURCE_DIR}/${src}") 75 | endif() 76 | if (NOT IS_ABSOLUTE "${dest}") 77 | set(dest "${CMAKE_INSTALL_PREFIX}/${dest}") 78 | endif() 79 | get_filename_component(src_name "${src}" NAME) 80 | get_filename_component(dest_name "${destname}" NAME) 81 | if (NOT EXISTS "${dest}/${dest_name}${suffix}") 82 | set(src_tmp "${CMAKE_CURRENT_BINARY_DIR}/${dest_name}${suffix}") 83 | configure_file(${src} ${src_tmp} @ONLY) 84 | install(FILES ${src_tmp} DESTINATION ${dest}) 85 | endif() 86 | endfunction() 87 | 88 | function(install_script src dest) 89 | if (NOT IS_ABSOLUTE "${src}") 90 | set(src "${CMAKE_CURRENT_SOURCE_DIR}/${src}") 91 | endif() 92 | if (NOT IS_ABSOLUTE "${dest}") 93 | set(dest "${CMAKE_INSTALL_PREFIX}/${dest}") 94 | endif() 95 | get_filename_component(src_name "${src}" NAME) 96 | set(src_tmp "${CMAKE_CURRENT_BINARY_DIR}/${src_name}") 97 | configure_file(${src} ${src_tmp} @ONLY) 98 | install(PROGRAMS ${src_tmp} DESTINATION ${dest}) 99 | endfunction() 100 | 101 | function(install_config src dest) 102 | if (NOT IS_ABSOLUTE "${src}") 103 | set(src "${CMAKE_CURRENT_SOURCE_DIR}/${src}") 104 | endif() 105 | if (NOT IS_ABSOLUTE "${dest}") 106 | set(dest "${CMAKE_INSTALL_PREFIX}/${dest}") 107 | endif() 108 | get_filename_component(src_name "${src}" NAME) 109 | set(src_tmp "${CMAKE_CURRENT_BINARY_DIR}/${src_name}") 110 | configure_file(${src} ${src_tmp} @ONLY) 111 | install(FILES ${src_tmp} DESTINATION ${dest}) 112 | endfunction() 113 | 114 | ############################# CONFIG SECTION ########################### 115 | 116 | # Prefer local include dirs to system ones. 117 | include_directories("${CMAKE_CURRENT_SOURCE_DIR}/" 118 | "${CMAKE_CURRENT_SOURCE_DIR}/src" 119 | "${CMAKE_CURRENT_SOURCE_DIR}/src/liblcb/include" 120 | "${CMAKE_BINARY_DIR}/src") 121 | 122 | set(TAR "tar") 123 | 124 | # Platform specific configuration. 125 | if (CMAKE_SYSTEM_NAME MATCHES "^.*BSD$|DragonFly") 126 | add_definitions(-D_BSD_SOURCE -DFREEBSD) 127 | message(STATUS "Configuring for BSD system") 128 | include_directories("/usr/local/include/") 129 | set(TAR "gtar") 130 | endif() 131 | 132 | if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") 133 | add_definitions(-D_BSD_SOURCE -DDARWIN) 134 | message(STATUS "Configuring for Darwin") 135 | set(TAR "gnutar") 136 | endif() 137 | 138 | 139 | if (CMAKE_SYSTEM_NAME STREQUAL "Linux") 140 | add_definitions(-D_GNU_SOURCE -DLINUX -D__USE_GNU=1) 141 | message(STATUS "Configuring for Linux") 142 | if (BUILD_CPU_MODE STREQUAL "32") 143 | add_definitions(-D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE) 144 | endif() 145 | 146 | list(APPEND CMAKE_REQUIRED_LIBRARIES rt) 147 | endif() 148 | 149 | 150 | try_c_flag(PIPE "-pipe") 151 | try_c_flag(NO_DEL_NULL_PTR_CHKS "-fno-delete-null-pointer-checks") 152 | 153 | 154 | if (CMAKE_BUILD_TYPE MATCHES "Debug") 155 | # Process with warn flags. 156 | try_c_flag(W "-W") 157 | try_c_flag(WALL "-Wall") 158 | try_c_flag(WEVERYTHING "-Weverything") 159 | if (NOT C_FLAG_WEVERYTHING) 160 | try_c_flag(WPOINTER "-Wpointer-arith") 161 | try_c_flag(WSTRICT_PROTOTYPES "-Wstrict-prototypes") 162 | try_c_flag(PEDANTIC "-pedantic") 163 | try_c_flag(WNULL_DEREFERENCE "-Wnull-dereference") 164 | try_c_flag(WDUPLICATED_COND "-Wduplicated-cond") 165 | try_c_flag(WIMPLICIT_FALLTHROUGH "-Wimplicit-fallthrough") 166 | endif() 167 | 168 | try_c_flag(WCAST_FN_TYPE_STRICT "-Wno-cast-function-type-strict") 169 | try_c_flag(WCAST_QUAL "-Wno-cast-qual") 170 | try_c_flag(WDATE_TIME "-Wno-date-time") 171 | try_c_flag(WDOCUMENTATION "-Wno-documentation") 172 | try_c_flag(WDOC_UNKNOWN_CMD "-Wno-documentation-unknown-command") 173 | try_c_flag(WFORMAT_NONLITERAL "-Wno-format-nonliteral") 174 | try_c_flag(WPACKED "-Wno-packed") 175 | try_c_flag(WPADDED "-Wno-padded") 176 | #try_c_flag(WPOINTER_SIGN "-Wno-pointer-sign") 177 | #try_c_flag(WRESERVED_ID_MACRO "-Wno-reserved-id-macro") 178 | try_c_flag(WRESERVED_IDENTIFIER "-Wno-reserved-identifier") 179 | #try_c_flag(WSIGN_COMPARE "-Wno-sign-compare") 180 | try_c_flag(WSWITCH_ENUM "-Wno-switch-enum") 181 | #try_c_flag(WUNUSED_CONST "-Wno-unused-const-variable") 182 | #try_c_flag(WUNUSED_FUNCTION "-Wno-unused-function") 183 | #try_c_flag(WUNUSED_PARAM "-Wno-unused-parameter") 184 | #try_c_flag(WUNUSED_VAR "-Wno-unused-variable") 185 | try_c_flag(WUNSAFE_BUFFER_USAGE "-Wno-unsafe-buffer-usage") 186 | try_c_flag(WVARIADIC_MACROS "-Wno-variadic-macros") 187 | try_c_flag(WGNU_ZERO_VAR_MACRO_ARGS "-Wno-gnu-zero-variadic-macro-arguments") 188 | try_c_flag(WZERO_LENGTH_ARRAY "-Wno-zero-length-array") 189 | 190 | set(CMAKE_INSTALL_DO_STRIP FALSE) 191 | set(CMAKE_C_OPT_FLAGS "-g3 -ggdb -O0") 192 | message(STATUS "Adding -DDEBUG to definitions.") 193 | add_definitions(-DDEBUG) 194 | else() 195 | set(CMAKE_BUILD_TYPE "Release") 196 | set(CMAKE_INSTALL_DO_STRIP TRUE) 197 | message(STATUS "Adding -DNDEBUG to definitions.") 198 | add_definitions(-DNDEBUG) 199 | endif() 200 | 201 | message(STATUS "Building in ${CMAKE_BUILD_TYPE} mode.") 202 | message(STATUS "CMAKE_INSTALL_DO_STRIP is ${CMAKE_INSTALL_DO_STRIP}.") 203 | 204 | 205 | if (NOT "${CMAKE_C_COMPILER_ID}" MATCHES SunPro) 206 | try_c_flag(STD11 "-std=c11") 207 | if (NOT C_FLAG_STD11) 208 | try_c_flag(STD99 "-std=c99") 209 | endif() 210 | endif() 211 | 212 | 213 | # Hardening flags 214 | try_c_flag(FORTIFY_SOURCE2 "-D_FORTIFY_SOURCE=2") 215 | try_c_flag(FSTACK_PROTECTOR_ALL "-fstack-protector-all") 216 | try_c_flag(FSANITIZE_SAFE_STACK "-fsanitize=safe-stack") 217 | try_c_flag(FSANITIZE_CFI "-fsanitize=cfi") 218 | try_c_flag(MRETPOLINE "-mretpoline") 219 | try_c_flag(MFUNCTION_RETURN "-mfunction-return=thunk") 220 | try_c_flag(MINDIRECT_BRANCH "-mindirect-branch=thunk") 221 | try_c_flag(FWRAPV "-fwrapv") 222 | try_c_flag(FPIE "-fPIE") 223 | if (C_FLAG_FPIE) 224 | try_linker_flag(PIE "-pie") 225 | endif() 226 | try_linker_flag(RETPOLINEPLT "-Wl,-z,retpolineplt") 227 | try_linker_flag(ZRELRO "-Wl,-z,relro") 228 | try_linker_flag(ZNOW "-Wl,-z,now") 229 | try_linker_flag(ZNOEXECSTACK "-Wl,-z,noexecstack") 230 | 231 | 232 | 233 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_C_OPT_FLAGS}") 234 | # Silently strip whitespace 235 | string(STRIP "${CMAKE_C_FLAGS}" CMAKE_C_FLAGS) 236 | string(STRIP "${CMAKE_EXE_LINKER_FLAGS}" CMAKE_EXE_LINKER_FLAGS) 237 | string(STRIP "${CMAKE_MODULE_LINKER_FLAGS}" CMAKE_MODULE_LINKER_FLAGS) 238 | string(STRIP "${CMAKE_SHARED_LINKER_FLAGS}" CMAKE_SHARED_LINKER_FLAGS) 239 | 240 | configure_file(config.h.cmake src/config.h) 241 | add_definitions(-DHAVE_CONFIG_H) 242 | 243 | ################################ SUBDIRS SECTION ####################### 244 | 245 | include(src/liblcb/CMakeLists.txt) 246 | add_subdirectory(src) 247 | 248 | if (ENABLE_TESTS) 249 | #add_subdirectory(tests) 250 | endif() 251 | 252 | ############################ TARGETS SECTION ########################### 253 | 254 | add_custom_target(dist ${CMAKE_CURRENT_SOURCE_DIR}/dist.sh 255 | "${CMAKE_BINARY_DIR}/${PACKAGE_TARNAME}.tar.xz" "${TAR}" 256 | COMMENT "Create source distribution" 257 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) 258 | 259 | ##################### INSTALLATION ##################################### 260 | 261 | # Configs 262 | install(CODE "FILE(MAKE_DIRECTORY ${CONFDIR})") 263 | 264 | install_if_not_exists(conf/msd_lite.conf ${CONFDIR} "msd_lite.conf" ".sample") 265 | 266 | if (CMAKE_SYSTEM_NAME MATCHES "^.*BSD$|DragonFly") 267 | install_script(os/freebsd/msd_lite "${CMAKE_INSTALL_PREFIX}/etc/rc.d/") 268 | endif() 269 | -------------------------------------------------------------------------------- /src/msd_lite_stat_text.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2012-2025 Rozhuk Ivan 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | * 26 | * Author: Rozhuk Ivan 27 | * 28 | */ 29 | 30 | 31 | #include 32 | #include 33 | //#include /* For mode constants */ 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #include 43 | #include 44 | #include 45 | #include /* snprintf, fprintf */ 46 | #include 47 | #include /* bcopy, bzero, memcpy, memmove, memset, strerror... */ 48 | 49 | #include "utils/macro.h" 50 | #include "utils/sys.h" 51 | #include "utils/io_buf.h" 52 | #include "threadpool/threadpool_task.h" 53 | #include "net/socket.h" 54 | #include "net/socket_address.h" 55 | #include "net/utils.h" 56 | #include "utils/buf_str.h" 57 | #include "proto/http_server.h" 58 | #include "stream_sys.h" 59 | #include "utils/info.h" 60 | #include "msd_lite_stat_text.h" 61 | 62 | 63 | 64 | static void gen_hub_stat_text_entry_enum_cb(tpt_p tpt, str_hub_p str_hub, 65 | void *udata); 66 | static void gen_hub_stat_text_enum_done_cb(tpt_p tpt, size_t send_msg_cnt, 67 | size_t error_cnt, void *udata); 68 | 69 | int 70 | gen_hub_stat_text_send_async(str_hubs_bckt_p shbskt, http_srv_cli_p cli) { 71 | int error; 72 | size_t tm; 73 | str_hubs_stat_t hstat; 74 | 75 | error = str_hubs_bckt_stat_summary(shbskt, &hstat); 76 | if (0 != error) 77 | return (error); 78 | tm = (16384 + 79 | (hstat.str_hub_count * 1024) + 80 | 1024 + 81 | (hstat.cli_count * (160 + 256 + 1024)) 82 | ); 83 | error = http_srv_cli_buf_realloc(cli, 0, tm); 84 | if (0 != error) /* Need more space! */ 85 | return (error); 86 | 87 | error = str_hubs_bckt_enum(shbskt, gen_hub_stat_text_entry_enum_cb, cli, 88 | gen_hub_stat_text_enum_done_cb); 89 | 90 | return (error); 91 | } 92 | static void 93 | gen_hub_stat_text_entry_enum_cb(tpt_p tpt, str_hub_p str_hub, void *udata) { 94 | http_srv_cli_p cli = udata; 95 | io_buf_p buf = http_srv_cli_get_buf(cli); 96 | str_hub_cli_p strh_cli, strh_cli_temp; 97 | time_t cur_time, time_conn; 98 | char straddr[STR_ADDR_LEN], straddr2[STR_ADDR_LEN], ifname[(IFNAMSIZ + 1)], str_time[64]; 99 | uint32_t i; 100 | size_t stm; 101 | //str_hub_src_conn_udp_tcp_p conn_udp_tcp; 102 | str_src_conn_mc_p conn_mc; 103 | 104 | cur_time = gettime_monotonic(); 105 | io_buf_printf(buf, 106 | "\r\n" 107 | "Stream hub: %s [thread: %zu @ cpu %i, clients: %zu, dropped clients: %"PRIu64"]\r\n", 108 | str_hub->name, 109 | tpt_get_num(tpt), tpt_get_cpu_id(tpt), 110 | str_hub->cli_count, str_hub->dropped_count); 111 | /* Sources. */ 112 | IO_BUF_COPYIN_CSTR(buf, " Source: multicast"); 113 | conn_mc = &str_hub->src_conn_params.mc; 114 | if (0 != sa_addr_port_to_str(&conn_mc->udp.addr, straddr, 115 | sizeof(straddr), NULL)) { 116 | memcpy(straddr, "", 19); 117 | } 118 | ifname[0] = 0; 119 | if_indextoname(conn_mc->if_index, ifname); 120 | io_buf_printf(buf, " %s@%s ", 121 | straddr, ifname); 122 | 123 | io_buf_printf(buf, 124 | "[state: OK, status: 0, rate: %"PRIu64"]\r\n", 125 | str_hub->baud_rate_in); 126 | 127 | /* Clients. */ 128 | TAILQ_FOREACH_SAFE(strh_cli, &str_hub->cli_head, next, strh_cli_temp) { 129 | if (0 != sa_addr_port_to_str(&strh_cli->remonte_addr, 130 | straddr, sizeof(straddr), NULL)) { 131 | memcpy(straddr, "", 19); 132 | } 133 | if (0 != sa_addr_port_to_str(&strh_cli->xreal_addr, 134 | straddr2, sizeof(straddr2), NULL)) { 135 | memcpy(straddr, "", 19); 136 | } 137 | //&cli_ud->xreal_addr 138 | time_conn = (cur_time - strh_cli->conn_time); 139 | fmt_as_uptime(&time_conn, str_time, sizeof(str_time)); 140 | 141 | if (0 != skt_get_tcp_cc(strh_cli->skt, 142 | ifname, sizeof(ifname), NULL)) { 143 | memcpy(ifname, "", 16); 144 | } 145 | if (0 != skt_get_tcp_maxseg(strh_cli->skt, (int*)&i)) { 146 | i = 0; 147 | } 148 | 149 | io_buf_printf(buf, 150 | " %s (%s) [conn time: %s, flags: %u, cc: %s, maxseg: %"PRIu32"] [user agent: %s]\r\n", 151 | straddr, straddr2, str_time, strh_cli->flags, ifname, i, 152 | (char*)strh_cli->user_agent 153 | ); 154 | /* Add soscket TCP stat. */ 155 | if (0 == skt_tcp_stat_text(strh_cli->skt, " ", 156 | (char*)IO_BUF_FREE_GET(buf), 157 | IO_BUF_FREE_SIZE(buf), &stm)) { 158 | IO_BUF_USED_INC(buf, stm); 159 | } 160 | } 161 | } 162 | static void 163 | gen_hub_stat_text_enum_done_cb(tpt_p tpt __unused, size_t send_msg_cnt __unused, 164 | size_t error_cnt, void *udata) { 165 | http_srv_cli_p cli = udata; 166 | http_srv_resp_p resp = http_srv_cli_get_resp(cli); 167 | static const char *cttype = "Content-Type: text/plain\r\n" 168 | "Pragma: no-cache"; 169 | 170 | if (0 == error_cnt) { 171 | resp->status_code = 200; 172 | resp->p_flags |= HTTP_SRV_RESP_P_F_CONTENT_LEN; 173 | resp->hdrs_count = 1; 174 | resp->hdrs[0].iov_base = MK_RW_PTR(cttype); 175 | resp->hdrs[0].iov_len = 42; 176 | } else { 177 | resp->status_code = 500; 178 | } 179 | http_srv_resume_responce(cli); 180 | } 181 | 182 | 183 | int 184 | gen_stat_text(const char *package_name, const char *package_version, 185 | str_hubs_bckt_p shbskt, info_sysres_p sysres, 186 | uint8_t *sysinfo, size_t sysinfo_size, uint8_t *syslimits, size_t syslimits_size, 187 | http_srv_cli_p cli) { 188 | int error; 189 | char straddr[STR_ADDR_LEN], start_time[64]; 190 | time_t time_work; 191 | size_t i, thread_cnt, tm; 192 | http_srv_p http_srv; 193 | tp_p tp; 194 | io_buf_p buf; 195 | struct tm stime; 196 | str_hubs_stat_t hstat, *stat; 197 | http_srv_stat_t http_srv_stat; 198 | 199 | 200 | error = str_hubs_bckt_stat_summary(shbskt, &hstat); 201 | if (0 != error) 202 | return (error); 203 | http_srv = http_srv_cli_get_srv(cli); 204 | error = http_srv_stat_get(http_srv, &http_srv_stat); 205 | if (0 != error) 206 | return (error); 207 | tp = http_srv_tp_get(http_srv); 208 | thread_cnt = tp_thread_count_max_get(tp); 209 | tm = (4096 + (4096 * thread_cnt) + syslimits_size + sysinfo_size); 210 | error = http_srv_cli_buf_realloc(cli, 0, tm); 211 | if (0 != error) /* Need more space! */ 212 | return (error); 213 | buf = http_srv_cli_get_buf(cli); 214 | 215 | time_work = (gettime_monotonic() - http_srv_stat.start_time_abs); 216 | if (0 == time_work) { /* Prevent division by zero. */ 217 | time_work ++; 218 | } 219 | /* Server stat. */ 220 | localtime_r(&http_srv_stat.start_time, &stime); 221 | strftime(start_time, sizeof(start_time), 222 | "%d.%m.%Y %H:%M:%S", &stime); 223 | fmt_as_uptime(&time_work, straddr, (sizeof(straddr) - 1)); 224 | io_buf_printf(buf, 225 | "Server: %s %s ("__DATE__" "__TIME__")\r\n" 226 | "start time: %s\r\n" 227 | "running time: %s\r\n" 228 | "connections online: %"PRIu64"\r\n" 229 | "timeouts: %"PRIu64"\r\n" 230 | "errors: %"PRIu64"\r\n" 231 | "HTTP errors: %"PRIu64"\r\n" 232 | "insecure requests: %"PRIu64"\r\n" 233 | "unhandled requests (404): %"PRIu64"\r\n" 234 | "requests per sec: %"PRIu64"\r\n" 235 | "requests total: %"PRIu64"\r\n" 236 | "\r\n\r\n", 237 | package_name, package_version, 238 | start_time, straddr, 239 | http_srv_stat.connections, 240 | http_srv_stat.timeouts, 241 | http_srv_stat.errors, 242 | http_srv_stat.http_errors, 243 | http_srv_stat.insecure_requests, 244 | http_srv_stat.unhandled_requests, 245 | (http_srv_stat.requests_total / (uint64_t)time_work), 246 | http_srv_stat.requests_total); 247 | io_buf_printf(buf, "Per Thread stat\r\n"); 248 | for (i = 0; i < thread_cnt; i ++) { 249 | /* Per Thread stat. */ 250 | stat = &shbskt->thr_data[i].stat; 251 | io_buf_printf(buf, 252 | "Thread: %zu @ cpu %i\r\n" 253 | "Stream hub count: %zu\r\n" 254 | "Clients count: %zu\r\n" 255 | "Rate in: %"PRIu64" mbps\r\n" 256 | "Rate out: %"PRIu64" mbps\r\n" 257 | "Total rate: %"PRIu64" mbps\r\n" 258 | "\r\n", 259 | i, tpt_get_cpu_id(tp_thread_get(tp, i)), 260 | stat->str_hub_count, 261 | stat->cli_count, 262 | ( stat->baud_rate_in / (1024 * 1024)), 263 | ( stat->baud_rate_out / (1024 * 1024)), 264 | (( stat->baud_rate_in + 265 | stat->baud_rate_out) / (1024 * 1024))); 266 | } 267 | /* Total stat. */ 268 | io_buf_printf(buf, 269 | "Summary\r\n" 270 | "Stream hub count: %zu\r\n" 271 | "Clients count: %zu\r\n" 272 | "Rate in: %"PRIu64" mbps\r\n" 273 | "Rate out: %"PRIu64" mbps\r\n" 274 | "Total rate: %"PRIu64" mbps\r\n" 275 | "\r\n\r\n", 276 | hstat.str_hub_count, 277 | hstat.cli_count, 278 | (hstat.baud_rate_in / (1024 * 1024)), 279 | (hstat.baud_rate_out / (1024 * 1024)), 280 | ((hstat.baud_rate_in + hstat.baud_rate_out) / (1024 * 1024))); 281 | 282 | error = info_sysres(sysres, (char*)IO_BUF_FREE_GET(buf), 283 | IO_BUF_FREE_SIZE(buf), &tm); 284 | if (0 != error) 285 | return (error); 286 | IO_BUF_USED_INC(buf, tm); 287 | 288 | io_buf_copyin(buf, syslimits, syslimits_size); 289 | 290 | IO_BUF_COPYIN_CRLFCRLF(buf); 291 | io_buf_copyin(buf, sysinfo, sysinfo_size); 292 | return (0); 293 | } 294 | -------------------------------------------------------------------------------- /msd_lite.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | mkdir -p $(IntermediateDirectory) 144 | echo '#define PACKAGE_NAME "msd_lite"' > $(IntermediateDirectory)/config.h 145 | echo '#define PACKAGE_VERSION "1.0.0"' >> $(IntermediateDirectory)/config.h 146 | echo '#define PACKAGE_DESCRIPTION "Multi stream daemon lite"' >> $(IntermediateDirectory)/config.h 147 | echo '#define PACKAGE_URL "https://github.com/rozhuk-im/msd_lite"' >> $(IntermediateDirectory)/config.h 148 | echo '#define PACKAGE_STRING PACKAGE_NAME" "PACKAGE_VERSION' >> $(IntermediateDirectory)/config.h 149 | 150 | 151 | 152 | ./configure --enable-debug 153 | make clean 154 | make 155 | make clean 156 | ./configure --enable-debug 157 | make 158 | 159 | 160 | 161 | None 162 | $(ProjectPath) 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | mkdir $(ProjectPath)/$(ConfigurationName) && 195 | cd $(ProjectPath)/$(ConfigurationName) && 196 | cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_VERBOSE_MAKEFILE=true .. 197 | 198 | rm -rf $(ProjectPath)/$(ConfigurationName) && 199 | mkdir $(ProjectPath)/$(ConfigurationName) && 200 | cd $(ProjectPath)/$(ConfigurationName) && 201 | cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_VERBOSE_MAKEFILE=true .. && 202 | make -C $(ProjectPath)/$(ConfigurationName) -j`getconf NPROCESSORS_ONLN` 203 | 204 | rm -rf $(ProjectPath)/$(ConfigurationName) 205 | make -C $(ProjectPath)/$(ConfigurationName) -j`getconf NPROCESSORS_ONLN` 206 | 207 | 208 | 209 | 210 | None 211 | $(WorkspacePath) 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | -------------------------------------------------------------------------------- /src/msd_lite.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2012-2025 Rozhuk Ivan 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | * 26 | * Author: Rozhuk Ivan 27 | * 28 | */ 29 | 30 | 31 | #include 32 | #include 33 | #include /* for demo mode */ 34 | //#include /* For mode constants */ 35 | 36 | #include 37 | #include 38 | //#include 39 | //#include 40 | #include 41 | #include 42 | 43 | #include 44 | #include 45 | #include 46 | #include /* snprintf, fprintf */ 47 | #include 48 | #include /* bcopy, bzero, memcpy, memmove, memset, strerror... */ 49 | #include /* malloc, exit */ 50 | #include /* close, write, sysconf */ 51 | #include /* open, fcntl */ 52 | #include /* SIGNAL constants. */ 53 | #include 54 | 55 | 56 | #include "utils/mem_utils.h" 57 | #include "utils/str2num.h" 58 | #include "proto/http.h" 59 | #include "utils/xml.h" 60 | 61 | #include "utils/macro.h" 62 | #include "utils/io_buf.h" 63 | #include "net/socket.h" 64 | #include "net/socket_address.h" 65 | #include "net/utils.h" 66 | #include "threadpool/threadpool_task.h" 67 | #include "utils/buf_str.h" 68 | #include "utils/sys.h" 69 | #include "proto/http_server.h" 70 | #include "utils/info.h" 71 | #include "utils/cmd_line_daemon.h" 72 | #include "utils/sys_res_limits_xml.h" 73 | #include "msd_lite_stat_text.h" 74 | 75 | 76 | 77 | #include "config.h" 78 | #undef PACKAGE_NAME 79 | #define PACKAGE_NAME "Multi stream daemon lite" 80 | #define CFG_FILE_MAX_SIZE (128 * 1024) 81 | 82 | 83 | 84 | struct prog_settings { 85 | http_srv_p http_srv; /* HTTP server. */ 86 | str_hubs_bckt_p shbskt; /* Stream hubs. */ 87 | 88 | uint8_t sysinfo[1024]; /* System info */ 89 | uint8_t syslimits[1024]; /* System limits */ 90 | size_t sysinfo_size; /* System info size */ 91 | size_t syslimits_size; /* System limits size */ 92 | info_sysres_t sysres; /* System resources statistic data. */ 93 | 94 | 95 | str_hub_settings_t hub_params; /* Stream hub params. */ 96 | str_src_settings_t src_params; /* Stream hub source params. */ 97 | str_src_conn_params_t src_conn_params; /* Stream hub source connection params. */ 98 | 99 | uintptr_t log_fd; // log file descriptor 100 | }; 101 | static struct prog_settings g_data; 102 | 103 | int msd_http_cust_hdrs_load(const uint8_t *buf, size_t buf_size, 104 | uint8_t **hdrs, size_t *hdrs_size_ret); 105 | int msd_hub_profile_load(const uint8_t *data, size_t data_size, 106 | str_hub_settings_p params); 107 | int msd_src_profile_load(const uint8_t *data, size_t data_size, 108 | str_src_settings_p params); 109 | int msd_src_conn_profile_load(const uint8_t *data, size_t data_size, 110 | void *conn_params); 111 | 112 | 113 | 114 | int msd_http_srv_hub_attach(http_srv_cli_p cli, 115 | uint8_t *hub_name, size_t hub_name_size, 116 | str_src_conn_params_p src_conn_params); 117 | uint32_t msd_http_req_url_parse(http_srv_req_p req, 118 | struct sockaddr_storage *ssaddr, 119 | uint32_t *if_index, uint32_t *rejoin_time, 120 | uint8_t *hub_name, size_t hub_name_size, 121 | size_t *hub_name_size_ret); 122 | 123 | 124 | static int msd_http_srv_on_req_rcv_cb(http_srv_cli_p cli, void *udata, 125 | http_srv_req_p req, http_srv_resp_p resp); 126 | 127 | 128 | #define MSD_CFG_CALC_VAL_COUNT(args...) \ 129 | xml_calc_tag_count_args(cfg_file_buf, cfg_file_buf_size, \ 130 | (const uint8_t*)"msd", ##args) 131 | #define MSD_CFG_GET_VAL_DATA(next_pos, data, data_size, args...) \ 132 | xml_get_val_args(cfg_file_buf, cfg_file_buf_size, next_pos, \ 133 | NULL, NULL, data, data_size, (const uint8_t*)"msd", ##args) 134 | #define MSD_CFG_GET_VAL_UINT(next_pos, val_ret, args...) \ 135 | xml_get_val_uint32_args(cfg_file_buf, cfg_file_buf_size, next_pos, \ 136 | val_ret, (const uint8_t*)"msd", ##args) 137 | #define MSD_CFG_GET_VAL_SIZE(next_pos, val_ret, args...) \ 138 | xml_get_val_size_t_args(cfg_file_buf, cfg_file_buf_size, next_pos, \ 139 | val_ret, (const uint8_t*)"msd", ##args) 140 | 141 | 142 | int 143 | msd_http_cust_hdrs_load(const uint8_t *buf, size_t buf_size, 144 | uint8_t **hdrs, size_t *hdrs_size_ret) { 145 | const uint8_t *cur_pos, *ptm; 146 | uint8_t *cur_w_pos; 147 | size_t tm, hdrs_size; 148 | 149 | if (NULL == buf || 0 == buf_size || NULL == hdrs || NULL == hdrs_size_ret) 150 | return (EINVAL); 151 | 152 | /* First pass: calc buffer size for headers. */ 153 | hdrs_size = 0; 154 | cur_pos = NULL; 155 | while (0 == xml_get_val_args(buf, buf_size, &cur_pos, NULL, NULL, 156 | &ptm, &tm, (const uint8_t*)"header", NULL)) { 157 | hdrs_size += (tm + 2); /* 2 = crlf. */ 158 | } 159 | 160 | if (0 == hdrs_size) { /* No custom headers. */ 161 | (*hdrs) = NULL; 162 | (*hdrs_size_ret) = 0; 163 | return (ESPIPE); 164 | } 165 | 166 | (*hdrs) = malloc((hdrs_size + sizeof(void*))); 167 | if (NULL == (*hdrs)) 168 | return (ENOMEM); 169 | /* Second pass: copy headers to buffer. */ 170 | cur_pos = NULL; 171 | cur_w_pos = (*hdrs); 172 | while (0 == xml_get_val_args(buf, buf_size, &cur_pos, NULL, NULL, 173 | &ptm, &tm, (const uint8_t*)"header", NULL)) { 174 | memcpy(cur_w_pos, ptm, tm); 175 | cur_w_pos += tm; 176 | memcpy(cur_w_pos, "\r\n", 2); 177 | cur_w_pos += 2; 178 | } 179 | (*hdrs)[hdrs_size] = 0; 180 | (*hdrs_size_ret) = hdrs_size; 181 | return (0); 182 | } 183 | 184 | int 185 | msd_hub_profile_load(const uint8_t *data, size_t data_size, str_hub_settings_p params) { 186 | const uint8_t *ptm; 187 | size_t tm; 188 | 189 | if (NULL == data || 0 == data_size || NULL == params) 190 | return (EINVAL); 191 | 192 | /* Read from config. */ 193 | if (0 == xml_get_val_args(data, data_size, NULL, NULL, NULL, &ptm, &tm, 194 | (const uint8_t*)"fDropSlowClients", NULL)) { 195 | yn_set_flag32(ptm, tm, STR_HUB_S_F_DROP_SLOW_CLI, ¶ms->flags); 196 | } 197 | if (0 == xml_get_val_args(data, data_size, NULL, NULL, NULL, &ptm, &tm, 198 | (const uint8_t*)"fSocketHalfClosed", NULL)) { 199 | yn_set_flag32(ptm, tm, STR_HUB_S_F_SKT_HALFCLOSED, ¶ms->flags); 200 | } 201 | if (0 == xml_get_val_args(data, data_size, NULL, NULL, NULL, &ptm, &tm, 202 | (const uint8_t*)"fSocketTCPNoDelay", NULL)) { 203 | yn_set_flag32(ptm, tm, STR_HUB_S_F_SKT_TCP_NODELAY, ¶ms->flags); 204 | } 205 | if (0 == xml_get_val_args(data, data_size, NULL, NULL, NULL, &ptm, &tm, 206 | (const uint8_t*)"fSocketTCPNoPush", NULL)) { 207 | yn_set_flag32(ptm, tm, STR_HUB_S_F_SKT_TCP_NOPUSH, ¶ms->flags); 208 | } 209 | 210 | xml_get_val_size_t_args(data, data_size, NULL, ¶ms->ring_buf_size, 211 | (const uint8_t*)"ringBufSize", NULL); 212 | xml_get_val_size_t_args(data, data_size, NULL, ¶ms->precache, 213 | (const uint8_t*)"precache", NULL); 214 | xml_get_val_size_t_args(data, data_size, NULL, ¶ms->snd_block_min_size, 215 | (const uint8_t*)"sndBlockSize", NULL); 216 | 217 | xml_get_val_uint32_args(data, data_size, NULL, ¶ms->skt_snd_buf, 218 | (const uint8_t*)"skt", "sndBuf", NULL); 219 | 220 | /* Load custom http headers. */ 221 | if (0 == xml_get_val_args(data, data_size, NULL, NULL, NULL, 222 | &ptm, &tm, (const uint8_t*)"headersList", NULL)) { 223 | msd_http_cust_hdrs_load(ptm, tm, ¶ms->cust_http_hdrs, 224 | ¶ms->cust_http_hdrs_size); 225 | } 226 | return (0); 227 | } 228 | 229 | int 230 | msd_src_profile_load(const uint8_t *data, size_t data_size, str_src_settings_p params) { 231 | 232 | if (NULL == data || 0 == data_size || NULL == params) 233 | return (EINVAL); 234 | 235 | /* Read from config. */ 236 | /* TODO: use socket_options.c */ 237 | xml_get_val_uint32_args(data, data_size, NULL, ¶ms->skt_rcv_buf, 238 | (const uint8_t*)"skt", "rcvBuf", NULL); 239 | xml_get_val_uint32_args(data, data_size, NULL, ¶ms->skt_rcv_lowat, 240 | (const uint8_t*)"skt", "rcvLoWatermark", NULL); 241 | xml_get_val_uint64_args(data, data_size, NULL, ¶ms->rcv_timeout, 242 | (const uint8_t*)"skt", "rcvTimeout", NULL); 243 | 244 | return (0); 245 | } 246 | 247 | int 248 | msd_src_conn_profile_load(const uint8_t *data, size_t data_size, void *conn) { 249 | const uint8_t *ptm = NULL; 250 | size_t tm = 0; 251 | char if_name[(IFNAMSIZ + 1)]; 252 | 253 | if (NULL == data || 0 == data_size || NULL == conn) 254 | return (EINVAL); 255 | 256 | /* Read from config. */ 257 | if (0 == xml_get_val_args(data, data_size, NULL, NULL, NULL, 258 | &ptm, &tm, (const uint8_t*)"udp", "address", NULL)) { 259 | sa_addr_port_from_str(&((str_src_conn_udp_p)conn)->addr, 260 | (const char*)ptm, tm); 261 | } 262 | if (0 == xml_get_val_args(data, data_size, NULL, NULL, NULL, 263 | &ptm, &tm, (const uint8_t*)"multicast", "ifName", NULL)) { 264 | memcpy(if_name, ptm, MIN(IFNAMSIZ, tm)); 265 | if_name[MIN(IFNAMSIZ, tm)] = 0; 266 | ((str_src_conn_mc_p)conn)->if_index = if_nametoindex(if_name); 267 | } 268 | xml_get_val_uint32_args(data, data_size, NULL, 269 | &((str_src_conn_mc_p)conn)->rejoin_time, 270 | (const uint8_t*)"multicast", "rejoinTime", NULL); 271 | 272 | return (0); 273 | } 274 | 275 | 276 | 277 | int 278 | main(int argc, char *argv[]) { 279 | int error = 0; 280 | uint8_t *cfg_file_buf = NULL; 281 | size_t tm, cfg_file_buf_size = 0; 282 | tp_p tp; 283 | cmd_line_data_t cmd_line_data; 284 | 285 | 286 | memset(&g_data, 0x00, sizeof(g_data)); 287 | if (0 != cmd_line_parse(argc, argv, &cmd_line_data)) { 288 | cmd_line_usage(PACKAGE_DESCRIPTION, PACKAGE_VERSION, 289 | "Rozhuk Ivan ", 290 | PACKAGE_URL); 291 | return (0); 292 | } 293 | if (0 != cmd_line_data.daemon) { 294 | make_daemon(); 295 | openlog(PACKAGE_NAME, 296 | (LOG_NDELAY | LOG_PID | ((0 != cmd_line_data.verbose) ? LOG_PERROR : 0)), 297 | LOG_DAEMON); 298 | } else { 299 | openlog(PACKAGE_NAME, 300 | (LOG_NDELAY | LOG_PID | LOG_PERROR), LOG_USER); 301 | } 302 | setlogmask(LOG_UPTO(cmd_line_data.log_level)); 303 | 304 | { /* Process config file. */ 305 | const uint8_t *data; 306 | size_t data_size; 307 | tp_settings_t tp_s; 308 | http_srv_cli_ccb_t ccb; 309 | http_srv_settings_t http_s; 310 | 311 | error = read_file(cmd_line_data.cfg_file_name, 0, 0, 0, 312 | CFG_FILE_MAX_SIZE, &cfg_file_buf, &cfg_file_buf_size); 313 | if (0 != error) { 314 | SYSLOG_ERR(LOG_CRIT, error, "config read_file()."); 315 | goto err_out; 316 | } 317 | if (0 != xml_get_val_args(cfg_file_buf, cfg_file_buf_size, 318 | NULL, NULL, NULL, NULL, NULL, 319 | (const uint8_t*)"msd", NULL)) { 320 | syslog(LOG_CRIT, "Config file XML format invalid."); 321 | goto err_out; 322 | } 323 | 324 | /* Log level. */ 325 | if (0 == MSD_CFG_GET_VAL_UINT(NULL, (uint32_t*)&cmd_line_data.log_level, 326 | "log", "level", NULL)) { 327 | setlogmask(LOG_UPTO(cmd_line_data.log_level)); 328 | } 329 | syslog(LOG_NOTICE, PACKAGE_STRING": started!"); 330 | #ifdef DEBUG 331 | syslog(LOG_INFO, "Build: "__DATE__" "__TIME__", DEBUG."); 332 | #else 333 | syslog(LOG_INFO, "Build: "__DATE__" "__TIME__", Release."); 334 | #endif 335 | syslog(LOG_INFO, "CPU count: %d.", get_cpu_count()); 336 | syslog(LOG_INFO, "Descriptor table size: %d (max files).", getdtablesize()); 337 | 338 | 339 | /* System resource limits. */ 340 | if (0 == MSD_CFG_GET_VAL_DATA(NULL, &data, &data_size, 341 | "systemResourceLimits", NULL)) { 342 | sys_res_limits_xml(data, data_size); 343 | } 344 | 345 | /* Thread pool settings. */ 346 | tp_settings_def(&tp_s); 347 | if (0 == MSD_CFG_GET_VAL_DATA(NULL, &data, &data_size, 348 | "threadPool", NULL)) { 349 | tp_settings_load_xml(data, data_size, &tp_s); 350 | } 351 | error = tp_create(&tp_s, &tp); 352 | if (0 != error) { 353 | SYSLOG_ERR(LOG_CRIT, error, "tp_create()."); 354 | goto err_out; 355 | } 356 | tp_threads_create(tp, 1); /* XXX exit rewrite. */ 357 | 358 | 359 | /* HTTP server settings. */ 360 | /* Read from config. */ 361 | if (0 != MSD_CFG_GET_VAL_DATA(NULL, &data, &data_size, "HTTP", NULL)) { 362 | syslog(LOG_NOTICE, "No HTTP server settings, nothink to do..."); 363 | goto err_out; 364 | } 365 | http_srv_def_settings(1, PACKAGE_NAME"/"PACKAGE_VERSION, 1, &http_s); 366 | http_s.req_p_flags = (HTTP_SRV_REQ_P_F_CONNECTION | HTTP_SRV_REQ_P_F_HOST | HTTP_SRV_REQ_P_F_HOST_ANY_PORT); 367 | http_s.resp_p_flags = (HTTP_SRV_RESP_P_F_CONN_CLOSE | HTTP_SRV_RESP_P_F_SERVER | HTTP_SRV_RESP_P_F_CONTENT_LEN); 368 | ccb.on_req_rcv = msd_http_srv_on_req_rcv_cb; 369 | ccb.on_rep_snd = NULL; 370 | ccb.on_destroy = NULL; 371 | 372 | error = http_srv_xml_load_start(data, data_size, tp, 373 | NULL, &ccb, &http_s, &g_data, 374 | &g_data.http_srv); 375 | if (0 != error) { 376 | SYSLOG_ERR(LOG_CRIT, error, "http_srv_xml_load_start()."); 377 | goto err_out; 378 | } 379 | 380 | /* Default settings. */ 381 | /* Stream hub defaults. */ 382 | g_data.hub_params.flags = STR_HUB_S_DEF_FLAGS; 383 | g_data.hub_params.skt_snd_buf = STR_HUB_S_DEF_SKT_SND_BUF; 384 | /* Stream source defaults params. */ 385 | str_src_conn_def(&g_data.src_conn_params); 386 | str_src_settings_def(&g_data.src_params); 387 | 388 | /* Stream hub params. */ 389 | if (0 == MSD_CFG_GET_VAL_DATA(NULL, &data, &data_size, 390 | "hubProfileList", "hubProfile", NULL)) { 391 | msd_hub_profile_load(data, data_size, &g_data.hub_params); 392 | } 393 | /* Stream source params. */ 394 | if (0 == MSD_CFG_GET_VAL_DATA(NULL, &data, &data_size, 395 | "sourceProfileList", "sourceProfile", NULL)) { 396 | msd_src_profile_load(data, data_size, &g_data.src_params); 397 | msd_src_conn_profile_load(data, data_size, &g_data.src_conn_params); 398 | } 399 | error = str_hubs_bckt_create(tp, PACKAGE_NAME"/"PACKAGE_VERSION, &g_data.hub_params, 400 | &g_data.src_params, &g_data.shbskt); 401 | if (0 != error) { 402 | SYSLOG_ERR(LOG_CRIT, error, "str_hubs_bckt_create()."); 403 | goto err_out; 404 | } 405 | free(cfg_file_buf); 406 | } /* Done with config. */ 407 | 408 | 409 | if (0 == info_limits((char*)g_data.syslimits, 410 | (sizeof(g_data.syslimits) - 1), &tm)) { 411 | g_data.syslimits_size = tm; 412 | } 413 | if (0 == info_sysinfo((char*)g_data.sysinfo, 414 | sizeof(g_data.sysinfo), &tm)) { 415 | g_data.sysinfo_size = tm; 416 | } 417 | info_sysres(&g_data.sysres, NULL, 0, NULL); 418 | 419 | tp_signal_handler_add_tp(tp); 420 | signal_install(tp_signal_handler); 421 | 422 | write_pid(cmd_line_data.pid_file_name); /* Store pid to file. */ 423 | set_user_and_group(cmd_line_data.pw_uid, cmd_line_data.pw_gid); /* Drop rights. */ 424 | 425 | /* Receive and process packets. */ 426 | tp_thread_attach_first(tp); 427 | tp_shutdown_wait(tp); 428 | 429 | /* Deinitialization... */ 430 | http_srv_shutdown(g_data.http_srv); /* No more new clients. */ 431 | http_srv_destroy(g_data.http_srv); /* AFTER radius is shut down! */ 432 | str_hubs_bckt_destroy(g_data.shbskt); 433 | if (NULL != cmd_line_data.pid_file_name) { 434 | unlink(cmd_line_data.pid_file_name); // Remove pid file 435 | } 436 | 437 | tp_destroy(tp); 438 | syslog(LOG_NOTICE, "Exiting."); 439 | closelog(); 440 | 441 | err_out: 442 | return (error); 443 | } 444 | 445 | 446 | /* 447 | * tcpcc = congestion ctrl name 448 | */ 449 | int 450 | msd_http_srv_hub_attach(http_srv_cli_p cli, uint8_t *hub_name, size_t hub_name_size, 451 | str_src_conn_params_p src_conn_params) { 452 | int error; 453 | str_hub_cli_p strh_cli; 454 | http_srv_req_p req; 455 | const uint8_t *ptm; 456 | size_t tm; 457 | tp_task_p tptask; 458 | uintptr_t skt; 459 | 460 | SYSLOGD_EX(LOG_DEBUG, "..."); 461 | 462 | if (NULL == cli || NULL == hub_name) 463 | return (EINVAL); 464 | 465 | tptask = http_srv_cli_get_tptask(cli); 466 | skt = tp_task_ident_get(tptask); 467 | /* Extract tcpCC, "User-Agent". */ 468 | req = http_srv_cli_get_req(cli); 469 | /* tcpcc. */ 470 | if (0 == http_query_val_get(req->line.query, req->line.query_size, 471 | (const uint8_t*)"tcpcc", 5, &ptm, &tm)) { 472 | skt_set_tcp_cc(skt, (const char*)ptm, tm); 473 | } 474 | /* Extract "User-Agent". */ 475 | if (0 != http_hdr_val_get(req->hdr, req->hdr_size, 476 | (const uint8_t*)"user-agent", 10, &ptm, &tm)) { 477 | ptm = NULL; 478 | tm = 0; 479 | } 480 | strh_cli = str_hub_cli_alloc(skt, (const char*)ptm, tm); 481 | if (NULL == strh_cli) { 482 | syslog(LOG_ERR, "str_hub_cli_alloc()."); 483 | return (ENOMEM); 484 | } 485 | /* 486 | * Set stream hub client data: some form http server client other from 487 | * http request. 488 | */ 489 | http_srv_cli_get_addr(cli, &strh_cli->remonte_addr); 490 | 491 | /* Client IP: get "X-Real-IP" from headers. */ 492 | if (0 != http_hdr_val_get(req->hdr, req->hdr_size, 493 | (const uint8_t*)"x-real-ip", 9, &ptm, &tm) || 494 | 0 != sa_addr_from_str(&strh_cli->xreal_addr, (const char*)ptm, tm) || 495 | 0 != sa_addr_is_loopback(&strh_cli->xreal_addr)) { /* No or bad addr. */ 496 | sa_copy(&strh_cli->remonte_addr, &strh_cli->xreal_addr); 497 | } 498 | 499 | SYSLOGD_EX(LOG_DEBUG, "%s - : attach...", hub_name); 500 | error = str_hub_cli_attach(g_data.shbskt, strh_cli, hub_name, hub_name_size, 501 | src_conn_params); 502 | /* Do not read/write to stream hub client, stream hub is new owner! */ 503 | if (0 != error) { 504 | strh_cli->skt = (uintptr_t)-1; 505 | str_hub_cli_destroy(NULL, strh_cli); 506 | SYSLOG_ERR(LOG_ERR, error, "str_hub_cli_attach()."); 507 | } else { 508 | tp_task_flags_del(tptask, TP_TASK_F_CLOSE_ON_DESTROY); 509 | http_srv_cli_free(cli); 510 | } 511 | return (error); 512 | } 513 | 514 | uint32_t 515 | msd_http_req_url_parse(http_srv_req_p req, struct sockaddr_storage *ssaddr, 516 | uint32_t *if_index, uint32_t *rejoin_time, 517 | uint8_t *hub_name, size_t hub_name_size, size_t *hub_name_size_ret) { 518 | const uint8_t *ptm; 519 | size_t tm; 520 | uint32_t ifindex, rejointime; 521 | char straddr[STR_ADDR_LEN], ifname[(IFNAMSIZ + 1)]; 522 | struct sockaddr_storage ss; 523 | 524 | SYSLOGD_EX(LOG_DEBUG, "..."); 525 | 526 | if (NULL == req || NULL == hub_name || 0 == hub_name_size) 527 | return (500); 528 | /* Get multicast address. */ 529 | if (0 != sa_addr_port_from_str(&ss, (const char*)(req->line.abs_path + 5), 530 | (req->line.abs_path_size - 5))) 531 | return (400); 532 | if (0 == sa_port_get(&ss)) { /* Def udp port. */ 533 | sa_port_set(&ss, 1234); 534 | } 535 | /* ifname, ifindex. */ 536 | if (0 == http_query_val_get(req->line.query, req->line.query_size, 537 | (const uint8_t*)"ifname", 6, &ptm, &tm) && IFNAMSIZ > tm) { 538 | memcpy(ifname, ptm, tm); 539 | ifname[tm] = 0; 540 | ifindex = if_nametoindex(ifname); 541 | } else { 542 | if (0 == http_query_val_get(req->line.query, 543 | req->line.query_size, (const uint8_t*)"ifindex", 7, 544 | &ptm, &tm)) { 545 | ifindex = ustr2u32(ptm, tm); 546 | } else { /* Default value. */ 547 | if (NULL != if_index) { 548 | ifindex = (*if_index); 549 | } else { 550 | ifindex = (uint32_t)-1; 551 | } 552 | } 553 | ifname[0] = 0; 554 | if_indextoname(ifindex, ifname); 555 | } 556 | 557 | /* rejoin_time. */ 558 | if (0 == http_query_val_get(req->line.query, 559 | req->line.query_size, (const uint8_t*)"rejoin_time", 11, 560 | &ptm, &tm)) { 561 | rejointime = ustr2u32(ptm, tm); 562 | } else { /* Default value. */ 563 | if (NULL != if_index) { 564 | rejointime = (*rejoin_time); 565 | } else { 566 | rejointime = 0; 567 | } 568 | } 569 | 570 | if (0 != sa_addr_port_to_str(&ss, straddr, sizeof(straddr), NULL)) 571 | return (400); 572 | tm = (size_t)snprintf((char*)hub_name, hub_name_size, 573 | "/udp/%s@%s", straddr, ifname); 574 | if (NULL != ssaddr) { 575 | sa_copy(&ss, ssaddr); 576 | } 577 | if (NULL != if_index) { 578 | (*if_index) = ifindex; 579 | } 580 | if (NULL != rejoin_time) { 581 | (*rejoin_time) = rejointime; 582 | } 583 | if (NULL != hub_name_size_ret) { 584 | (*hub_name_size_ret) = tm; 585 | } 586 | 587 | return (200); 588 | } 589 | 590 | 591 | 592 | /* http request from client is received now, process it. */ 593 | /* http_srv_on_req_rcv_cb */ 594 | static int 595 | msd_http_srv_on_req_rcv_cb(http_srv_cli_p cli, void *udata __unused, 596 | http_srv_req_p req, http_srv_resp_p resp) { 597 | size_t buf_size; 598 | int error; 599 | uint8_t buf[512]; 600 | str_src_conn_params_t src_conn_params; 601 | static const char *cttype = "Content-Type: text/plain\r\n" 602 | "Pragma: no-cache"; 603 | 604 | SYSLOGD_EX(LOG_DEBUG, "..."); 605 | 606 | if (HTTP_REQ_METHOD_GET != req->line.method_code && 607 | HTTP_REQ_METHOD_HEAD != req->line.method_code) { 608 | resp->status_code = 400; 609 | return (HTTP_SRV_CB_CONTINUE); 610 | } 611 | if (0 == (req->flags & HTTP_SRV_RD_F_HOST_IS_LOCAL)) { 612 | resp->status_code = 403; 613 | return (HTTP_SRV_CB_CONTINUE); 614 | } 615 | 616 | /* Statistic request. */ 617 | if (HTTP_REQ_METHOD_GET == req->line.method_code && 618 | 0 == mem_cmpin_cstr("/stat", req->line.abs_path, req->line.abs_path_size)) { 619 | error = gen_stat_text(PACKAGE_NAME, PACKAGE_VERSION, 620 | g_data.shbskt, &g_data.sysres, 621 | (uint8_t*)g_data.sysinfo, g_data.sysinfo_size, 622 | (uint8_t*)g_data.syslimits, g_data.syslimits_size, cli); 623 | if (0 == error) { 624 | resp->status_code = 200; 625 | resp->hdrs_count = 1; 626 | resp->hdrs[0].iov_base = MK_RW_PTR(cttype); 627 | resp->hdrs[0].iov_len = 42; 628 | } else { 629 | resp->status_code = 500; 630 | } 631 | return (HTTP_SRV_CB_CONTINUE); 632 | } 633 | /* Stream Hub statistic request. */ 634 | if (HTTP_REQ_METHOD_GET == req->line.method_code && 635 | 7 < req->line.abs_path_size && 636 | 0 == mem_cmpi_cstr("/hubstat", req->line.abs_path)) { 637 | error = gen_hub_stat_text_send_async(g_data.shbskt, cli); 638 | if (0 != error) { 639 | resp->status_code = 500; 640 | return (HTTP_SRV_CB_CONTINUE); 641 | } 642 | /* Will send reply later... */ 643 | return (HTTP_SRV_CB_NONE); 644 | } 645 | 646 | if (12 < req->line.abs_path_size && 647 | (0 == memcmp(req->line.abs_path, "/udp/", 5) || 648 | 0 == memcmp(req->line.abs_path, "/rtp/", 5))) { 649 | /* Default value. */ 650 | memcpy(&src_conn_params, &g_data.src_conn_params, sizeof(str_src_conn_mc_t)); 651 | /* Get multicast address, ifindex, hub name. */ 652 | resp->status_code = msd_http_req_url_parse(req, 653 | &src_conn_params.udp.addr, 654 | &src_conn_params.mc.if_index, 655 | &src_conn_params.mc.rejoin_time, 656 | buf, sizeof(buf), &buf_size); 657 | if (200 != resp->status_code) 658 | return (HTTP_SRV_CB_CONTINUE); 659 | if (HTTP_REQ_METHOD_HEAD == req->line.method_code) { 660 | /* Send HTTP headers only... */ 661 | resp->status_code = 200; 662 | resp->p_flags &= ~HTTP_SRV_RESP_P_F_CONTENT_LEN; 663 | if (6 < g_data.hub_params.cust_http_hdrs_size) { 664 | resp->hdrs_count = 1; 665 | resp->hdrs[0].iov_base = g_data.hub_params.cust_http_hdrs; 666 | resp->hdrs[0].iov_len = g_data.hub_params.cust_http_hdrs_size; 667 | } 668 | return (HTTP_SRV_CB_CONTINUE); 669 | } 670 | if (0 != msd_http_srv_hub_attach(cli, buf, buf_size, &src_conn_params)) { 671 | resp->status_code = 500; 672 | return (HTTP_SRV_CB_CONTINUE); 673 | } 674 | /* Will send reply later... */ 675 | return (HTTP_SRV_CB_NONE); 676 | } /* "/udp/" / "/rtp/" */ 677 | 678 | /* URL not found. */ 679 | resp->status_code = 404; 680 | 681 | return (HTTP_SRV_CB_CONTINUE); 682 | } 683 | -------------------------------------------------------------------------------- /src/stream_sys.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2012-2025 Rozhuk Ivan 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | * 26 | * Author: Rozhuk Ivan 27 | * 28 | */ 29 | 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include /* For mode constants */ 36 | #include /* flock */ 37 | 38 | #include /* malloc, exit */ 39 | #include 40 | #include /* snprintf, fprintf */ 41 | #include /* close, write, sysconf */ 42 | #include /* For O_* constants */ 43 | #include /* bcopy, bzero, memcpy, memmove, memset, strerror... */ 44 | #include 45 | #include 46 | #include 47 | 48 | #include "utils/macro.h" 49 | #include "utils/sys.h" 50 | #include "threadpool/threadpool.h" 51 | #include "threadpool/threadpool_msg_sys.h" 52 | #include "threadpool/threadpool_task.h" 53 | #include "net/socket.h" 54 | #include "net/socket_address.h" 55 | #include "net/utils.h" 56 | #include "utils/info.h" 57 | #include "utils/buf_str.h" 58 | #include "proto/rtp.h" 59 | #include "proto/mpeg2ts.h" 60 | #include "crypto/hash/md5.h" 61 | #include "utils/mem_utils.h" 62 | #include "proto/http.h" 63 | #include "stream_sys.h" 64 | 65 | /* Internal constants. */ 66 | #define STR_HUB_CLI_RECV_BUF 4096 67 | #define STR_HUB_CLI_RECV_LOWAT 1 68 | #define STR_SRC_UDP_PKT_SIZE_STD 1500 69 | #define STR_SRC_UDP_PKT_SIZE_MAX 65612 /* 349 * 188 */ 70 | 71 | 72 | typedef struct str_hub_cli_attach_cb_data_s { 73 | str_hubs_bckt_p shbskt; 74 | str_hub_cli_p strh_cli; 75 | uint8_t *hub_name; 76 | size_t hub_name_size; 77 | str_src_conn_params_t src_conn_params; 78 | } str_hub_cli_attach_cb_data_t, *str_hub_cli_attach_cb_data_p; 79 | 80 | 81 | 82 | typedef struct str_hubs_bckt_enum_data_s { /* thread message sync data. */ 83 | str_hubs_bckt_p shbskt; 84 | str_hubs_bckt_enum_cb enum_cb; 85 | void *udata; 86 | tpt_msg_done_cb done_cb; 87 | } str_hubs_bckt_enum_data_t, *str_hubs_bckt_enum_data_p; 88 | 89 | 90 | 91 | tpt_p str_hub_tpt_get_by_name(tp_p tp, const uint8_t *name, size_t name_size); 92 | 93 | static void str_hubs_bckt_destroy_msg_cb(tpt_p tpt, void *udata); 94 | 95 | static void str_hubs_bckt_enum_msg_cb(tpt_p tpt, void *udata); 96 | static void str_hubs_bckt_enum_done_cb(tpt_p tpt, size_t send_msg_cnt, 97 | size_t error_cnt, void *udata); 98 | 99 | void str_hubs_bckt_timer_service(str_hubs_bckt_p shbskt, 100 | str_hub_p str_hub, str_hubs_stat_p stat); 101 | static void str_hubs_bckt_timer_msg_cb(tpt_p tpt, void *udata); 102 | static void str_hubs_bckt_timer_cb(tp_event_p ev, tp_udata_p tp_udata); 103 | 104 | int str_hub_create_int(str_hubs_bckt_p shbskt, tpt_p tpt, 105 | uint8_t *name, size_t name_size, 106 | str_src_conn_params_p src_conn_params, str_hub_p *str_hub_ret); 107 | void str_hub_destroy_int(str_hub_p str_hub); 108 | 109 | void str_hub_cli_attach_msg_cb(tpt_p tpt, void *udata); 110 | 111 | int str_hub_send_to_client(str_hub_p str_hub, str_hub_cli_p strh_cli, 112 | size_t *transfered_size); 113 | int str_hub_send_to_clients(str_hub_p str_hub); 114 | static int str_src_recv_mc_cb(tp_task_p tptask, int error, uint32_t eof, 115 | size_t data2transfer_size, void *arg); 116 | int str_src_r_buf_alloc(str_hub_p str_hub); 117 | void str_src_r_buf_free(str_hub_p str_hub); 118 | 119 | 120 | 121 | /* XXX Thread pool balancer */ 122 | tpt_p 123 | str_hub_tpt_get_by_name(tp_p tp, const uint8_t *name, size_t name_size) { 124 | size_t thread_num, thread_cnt; 125 | uint8_t hash[MD5_HASH_SIZE]; 126 | 127 | md5_get_digest(name, name_size, hash); 128 | 129 | thread_cnt = tp_thread_count_max_get(tp); 130 | //thread_num = (/*(hash / thread_cnt) ^*/ (hash % thread_cnt)); 131 | thread_num = ((thread_cnt * data_xor8(hash, sizeof(hash))) / 256); 132 | if (thread_cnt < thread_num) { 133 | thread_num = (thread_cnt - 1); 134 | } 135 | 136 | return (tp_thread_get(tp, thread_num)); 137 | } 138 | 139 | 140 | void 141 | str_hub_settings_def(str_hub_settings_p p_ret) { 142 | 143 | if (NULL == p_ret) 144 | return; 145 | memset(p_ret, 0x00, sizeof(str_hub_settings_t)); 146 | p_ret->flags = STR_HUB_S_DEF_FLAGS; 147 | p_ret->ring_buf_size = STR_HUB_S_DEF_RING_BUF_SIZE; 148 | p_ret->precache = STR_HUB_S_DEF_PRECAHE; 149 | p_ret->snd_block_min_size = STR_HUB_S_DEF_SND_BLOCK_MIN_SIZE; 150 | p_ret->skt_snd_buf = STR_HUB_S_DEF_SKT_SND_BUF; 151 | } 152 | 153 | void 154 | str_src_settings_def(str_src_settings_p p_ret) { 155 | 156 | if (NULL == p_ret) 157 | return; 158 | memset(p_ret, 0x00, sizeof(str_src_settings_t)); 159 | p_ret->skt_rcv_buf = STR_SRC_S_DEF_SKT_RCV_BUF; 160 | p_ret->skt_rcv_lowat = STR_SRC_S_DEF_SKT_RCV_LOWAT; 161 | p_ret->rcv_timeout = STR_SRC_S_DEF_UDP_RCV_TIMEOUT; 162 | } 163 | 164 | void 165 | str_src_conn_def(str_src_conn_params_p src_conn_params) { 166 | 167 | if (NULL == src_conn_params) 168 | return; 169 | memset(src_conn_params, 0x00, sizeof(str_src_conn_params_t)); 170 | src_conn_params->mc.if_index = STR_SRC_CONN_DEF_IFINDEX; 171 | src_conn_params->mc.rejoin_time = 0; 172 | } 173 | 174 | 175 | int 176 | str_hubs_bckt_create(tp_p tp, const char *app_ver, str_hub_settings_p hub_params, 177 | str_src_settings_p src_params, str_hubs_bckt_p *shbskt_ret) { 178 | int error; 179 | str_hubs_bckt_p shbskt; 180 | char osver[128]; 181 | size_t i, thread_count_max; 182 | 183 | if (NULL == shbskt_ret) 184 | return (EINVAL); 185 | shbskt = calloc(1, sizeof(str_hubs_bckt_t) + hub_params->cust_http_hdrs_size + sizeof(void*)); 186 | if (NULL == shbskt) 187 | return (ENOMEM); 188 | thread_count_max = tp_thread_count_max_get(tp); 189 | shbskt->thr_data = calloc(1, (sizeof(str_hub_thrd_t) * thread_count_max)); 190 | if (NULL == shbskt->thr_data) { 191 | error = ENOMEM; 192 | goto err_out; 193 | } 194 | for (i = 0; i < thread_count_max; i ++) { 195 | TAILQ_INIT(&shbskt->thr_data[i].hub_head); 196 | } 197 | /* Stream Hub Params */ 198 | memcpy(&shbskt->hub_params, hub_params, sizeof(str_hub_settings_t)); 199 | /* Copy custom HTTP headers to new buffer. */ 200 | shbskt->hub_params.cust_http_hdrs = (uint8_t*)(shbskt + 1); 201 | if (NULL != hub_params->cust_http_hdrs && 202 | 0 != hub_params->cust_http_hdrs_size) { 203 | /* Custom headers body. */ 204 | memcpy(shbskt->hub_params.cust_http_hdrs, 205 | hub_params->cust_http_hdrs, 206 | hub_params->cust_http_hdrs_size); 207 | if (0 != memcmp((shbskt->hub_params.cust_http_hdrs + 208 | (shbskt->hub_params.cust_http_hdrs_size - 2)), "\r\n", 2)) { 209 | /* Add CRLF. */ 210 | memcpy((shbskt->hub_params.cust_http_hdrs + 211 | shbskt->hub_params.cust_http_hdrs_size), "\r\n", 2); 212 | shbskt->hub_params.cust_http_hdrs_size += 2; 213 | } 214 | } 215 | /* Add final CRLF + zero. */ 216 | memcpy((shbskt->hub_params.cust_http_hdrs + 217 | shbskt->hub_params.cust_http_hdrs_size), "\r\n", 3); 218 | shbskt->hub_params.cust_http_hdrs_size += 2; 219 | /* sec->ms, kb -> bytes */ 220 | hub_params = &shbskt->hub_params; /* Use short name. */ 221 | hub_params->ring_buf_size *= 1024; 222 | hub_params->precache *= 1024; 223 | hub_params->snd_block_min_size *= 1024; 224 | hub_params->skt_snd_buf *= 1024; 225 | /* Correct values. */ 226 | if (hub_params->precache > hub_params->ring_buf_size) { 227 | hub_params->precache = hub_params->ring_buf_size; 228 | } 229 | if (hub_params->snd_block_min_size > hub_params->skt_snd_buf) { 230 | hub_params->snd_block_min_size = hub_params->skt_snd_buf; 231 | } 232 | 233 | /* Stream src Params */ 234 | memcpy(&shbskt->src_params, src_params, sizeof(str_src_settings_t)); 235 | /* Use short name. */ 236 | src_params = &shbskt->src_params; 237 | /* Correct values. */ 238 | src_params->skt_rcv_lowat = MIN(src_params->skt_rcv_lowat, src_params->skt_rcv_buf); 239 | /* sec->ms, kb -> bytes */ 240 | src_params->skt_rcv_buf *= 1024; 241 | src_params->skt_rcv_lowat *= 1024; 242 | //src_params->rcv_timeout =; // In seconds! 243 | 244 | /* Base HTTP headers. */ 245 | if (0 != info_get_os_ver("/", 1, osver, 246 | (sizeof(osver) - 1), NULL)) 247 | memcpy(osver, "Generic OS/1.0", 15); 248 | shbskt->base_http_hdrs_size = (size_t)snprintf((char*)shbskt->base_http_hdrs, 249 | sizeof(shbskt->base_http_hdrs), 250 | "Server: %s %s HTTP stream hub by Rozhuk Ivan\r\n" 251 | "Connection: close\r\n", 252 | osver, app_ver); 253 | /* Timer */ 254 | shbskt->tp = tp; 255 | shbskt->service_tmr.cb_func = str_hubs_bckt_timer_cb; 256 | shbskt->service_tmr.ident = (uintptr_t)shbskt; 257 | error = tpt_ev_add_args(tp_thread_get_rr(shbskt->tp), TP_EV_TIMER, 258 | 0, TP_FF_T_MSEC, 1000 /* 1 sec. */, &shbskt->service_tmr); 259 | if (0 != error) { 260 | SYSLOG_ERR(LOG_ERR, error, "tpt_ev_add_args()."); 261 | goto err_out; 262 | } 263 | 264 | (*shbskt_ret) = shbskt; 265 | 266 | return (0); 267 | 268 | err_out: 269 | free(shbskt->thr_data); 270 | free(shbskt); 271 | return (error); 272 | } 273 | 274 | void 275 | str_hubs_bckt_destroy(str_hubs_bckt_p shbskt) { 276 | 277 | if (NULL == shbskt) 278 | return; 279 | tpt_ev_del_args1(TP_EV_TIMER, &shbskt->service_tmr); 280 | /* Broadcast to all threads. */ 281 | tpt_msg_bsend(shbskt->tp, NULL, 282 | (TP_MSG_F_SELF_DIRECT | TP_MSG_F_FORCE | TP_MSG_F_FAIL_DIRECT | TP_BMSG_F_SYNC), 283 | str_hubs_bckt_destroy_msg_cb, shbskt); 284 | 285 | free(shbskt->thr_data); 286 | free(shbskt); 287 | } 288 | static void 289 | str_hubs_bckt_destroy_msg_cb(tpt_p tpt, void *udata) { 290 | str_hubs_bckt_p shbskt = (str_hubs_bckt_p)udata; 291 | str_hub_p str_hub, str_hub_temp; 292 | size_t thread_num; 293 | 294 | //SYSLOGD_EX(LOG_DEBUG, "..."); 295 | thread_num = tpt_get_num(tpt); 296 | 297 | TAILQ_FOREACH_SAFE(str_hub, &shbskt->thr_data[thread_num].hub_head, next, 298 | str_hub_temp) { 299 | str_hub_destroy_int(str_hub); 300 | } 301 | } 302 | 303 | 304 | int 305 | str_hubs_bckt_enum(str_hubs_bckt_p shbskt, str_hubs_bckt_enum_cb enum_cb, 306 | void *udata, tpt_msg_done_cb done_cb) { 307 | int error; 308 | str_hubs_bckt_enum_data_p enum_data; 309 | 310 | if (NULL == shbskt || NULL == enum_cb) 311 | return (EINVAL); 312 | enum_data = malloc(sizeof(str_hubs_bckt_enum_data_t)); 313 | if (NULL == enum_data) 314 | return (ENOMEM); 315 | enum_data->shbskt = shbskt; 316 | enum_data->enum_cb = enum_cb; 317 | enum_data->udata = udata; 318 | enum_data->done_cb = done_cb; 319 | 320 | error = tpt_msg_cbsend(shbskt->tp, NULL, 321 | (TP_CBMSG_F_ONE_BY_ONE), str_hubs_bckt_enum_msg_cb, 322 | enum_data, str_hubs_bckt_enum_done_cb); 323 | if (0 != error) { 324 | free(enum_data); 325 | } 326 | 327 | return (error); 328 | } 329 | static void 330 | str_hubs_bckt_enum_msg_cb(tpt_p tpt, void *udata) { 331 | str_hubs_bckt_enum_data_p enum_data = udata; 332 | str_hubs_bckt_p shbskt = enum_data->shbskt; 333 | str_hub_p str_hub, str_hub_temp; 334 | size_t thread_num; 335 | 336 | //SYSLOGD_EX(LOG_DEBUG, "..."); 337 | thread_num = tpt_get_num(tpt); 338 | 339 | TAILQ_FOREACH_SAFE(str_hub, &shbskt->thr_data[thread_num].hub_head, next, 340 | str_hub_temp) { 341 | enum_data->enum_cb(tpt, str_hub, enum_data->udata); 342 | } 343 | } 344 | static void 345 | str_hubs_bckt_enum_done_cb(tpt_p tpt, size_t send_msg_cnt, size_t error_cnt, 346 | void *udata) { 347 | str_hubs_bckt_enum_data_p enum_data = udata; 348 | 349 | if (NULL != enum_data->done_cb) 350 | enum_data->done_cb(tpt, send_msg_cnt, error_cnt, enum_data->udata); 351 | free(enum_data); 352 | } 353 | 354 | 355 | int 356 | str_hubs_bckt_stat_summary(str_hubs_bckt_p shbskt, str_hubs_stat_p stat) { 357 | size_t i, thread_cnt; 358 | 359 | if (NULL == shbskt || NULL == stat) 360 | return (EINVAL); 361 | thread_cnt = tp_thread_count_max_get(shbskt->tp); 362 | memset(stat, 0x00, sizeof(str_hubs_stat_t)); 363 | for (i = 0; i < thread_cnt; i ++) { 364 | stat->str_hub_count += shbskt->thr_data[i].stat.str_hub_count; 365 | stat->cli_count += shbskt->thr_data[i].stat.cli_count; 366 | stat->baud_rate_in += shbskt->thr_data[i].stat.baud_rate_in; 367 | stat->baud_rate_out += shbskt->thr_data[i].stat.baud_rate_out; 368 | } 369 | return (0); 370 | } 371 | 372 | 373 | void 374 | str_hubs_bckt_timer_service(str_hubs_bckt_p shbskt, str_hub_p str_hub, 375 | str_hubs_stat_p stat) { 376 | int error; 377 | str_src_settings_p src_params = &shbskt->src_params; 378 | struct timespec *tp = &shbskt->tp_last_tmr_next; 379 | uint64_t tm64; 380 | time_t tmt; 381 | 382 | 383 | /* Stat update. */ 384 | /* Update stream hub clients baud rate. */ 385 | if (0 == (tp->tv_sec & 1)) { /* every 2 second */ 386 | tm64 = (1000000000 * ((uint64_t)tp->tv_sec - (uint64_t)shbskt->tp_last_tmr.tv_sec)); 387 | tm64 += ((uint64_t)tp->tv_nsec - (uint64_t)shbskt->tp_last_tmr.tv_nsec); 388 | if (0 == tm64) /* Prevent division by zero. */ 389 | tm64 ++; 390 | str_hub->baud_rate_out = ((str_hub->sended_count * 4000000000) / tm64); 391 | str_hub->baud_rate_in = ((str_hub->received_count * 4000000000) / tm64); 392 | str_hub->sended_count = 0; 393 | str_hub->received_count = 0; 394 | } 395 | /* Per Thread stat. */ 396 | stat->str_hub_count ++; 397 | stat->cli_count += str_hub->cli_count; 398 | stat->baud_rate_out += str_hub->baud_rate_out; 399 | stat->baud_rate_in += str_hub->baud_rate_in; 400 | 401 | /* Check hub. */ 402 | if (0 == str_hub->cli_count) { 403 | syslog(LOG_INFO, "%s: No more clients, selfdestroy.", 404 | str_hub->name); 405 | str_hub_destroy_int(str_hub); 406 | return; 407 | } 408 | /* No traffic check. */ 409 | if (0 != src_params->rcv_timeout) { 410 | tmt = (str_hub->tp_last_recv.tv_sec + (time_t)src_params->rcv_timeout); 411 | if (tmt < tp->tv_sec || 412 | (tmt == tp->tv_sec && str_hub->tp_last_recv.tv_nsec < tp->tv_nsec)) { 413 | str_hub_destroy_int(str_hub); 414 | return; 415 | } 416 | } 417 | /* Re join multicast group timer. */ 418 | if (0 != str_hub->src_conn_params.mc.rejoin_time && 419 | str_hub->next_rejoin_time < tp->tv_sec) { 420 | str_hub->next_rejoin_time = (tp->tv_sec + (time_t)str_hub->src_conn_params.mc.rejoin_time); 421 | for (int join = 0; join < 2; join ++) { 422 | error = skt_mc_join(tp_task_ident_get(str_hub->tptask), join, 423 | str_hub->src_conn_params.mc.if_index, 424 | &str_hub->src_conn_params.mc.udp.addr); 425 | SYSLOG_ERR(LOG_ERR, error, "skt_mc_join()."); 426 | } 427 | } 428 | } 429 | static void 430 | str_hubs_bckt_timer_msg_cb(tpt_p tpt, void *udata) { 431 | str_hubs_bckt_p shbskt = (str_hubs_bckt_p)udata; 432 | str_hub_p str_hub, str_hub_temp; 433 | str_hubs_stat_t stat; 434 | size_t thread_num; 435 | 436 | //SYSLOGD_EX(LOG_DEBUG, "..."); 437 | thread_num = tpt_get_num(tpt); 438 | memset(&stat, 0x00, sizeof(str_hubs_stat_t)); 439 | 440 | /* Enum all Stream Hubs associated with this thread. */ 441 | TAILQ_FOREACH_SAFE(str_hub, &shbskt->thr_data[thread_num].hub_head, next, 442 | str_hub_temp) { 443 | str_hubs_bckt_timer_service(shbskt, str_hub, &stat); 444 | } 445 | /* Update stat. */ 446 | memcpy(&shbskt->thr_data[thread_num].stat, &stat, sizeof(str_hubs_stat_t)); 447 | } 448 | static void 449 | str_hubs_bckt_timer_cb(tp_event_p ev __unused, tp_udata_p tp_udata) { 450 | str_hubs_bckt_p shbskt = (str_hubs_bckt_p)tp_udata->ident; 451 | 452 | //SYSLOGD_EX(LOG_DEBUG, "..."); 453 | if (NULL == shbskt) 454 | return; 455 | memcpy(&shbskt->tp_last_tmr, &shbskt->tp_last_tmr_next, sizeof(struct timespec)); 456 | clock_gettime(CLOCK_MONOTONIC_FAST, &shbskt->tp_last_tmr_next); 457 | /* Broadcast to all threads. */ 458 | tpt_msg_bsend(shbskt->tp, tp_udata->tpt, 459 | TP_MSG_F_SELF_DIRECT, str_hubs_bckt_timer_msg_cb, shbskt); 460 | } 461 | 462 | 463 | int 464 | str_hub_create_int(str_hubs_bckt_p shbskt, tpt_p tpt, uint8_t *name, size_t name_size, 465 | str_src_conn_params_p src_conn_params, str_hub_p *str_hub_ret) { 466 | int error; 467 | str_hub_p str_hub; 468 | uintptr_t skt; 469 | str_src_settings_p src_params; 470 | str_src_conn_udp_p conn_udp; 471 | 472 | SYSLOGD_EX(LOG_DEBUG, "..."); 473 | 474 | if (NULL == shbskt || NULL == name || 0 == name_size || NULL == str_hub_ret) 475 | return (EINVAL); 476 | str_hub = calloc(1, (sizeof(str_hub_t) + name_size + sizeof(void*))); 477 | if (NULL == str_hub) 478 | return (ENOMEM); 479 | 480 | str_hub->shbskt = shbskt; 481 | str_hub->name = (uint8_t*)(str_hub + 1); 482 | str_hub->name_size = name_size; 483 | memcpy(str_hub->name, name, name_size); 484 | TAILQ_INIT(&str_hub->cli_head); 485 | str_hub->tpt = tpt; 486 | clock_gettime(CLOCK_MONOTONIC_FAST, &str_hub->tp_last_recv); 487 | str_hub->r_buf_fd = (uintptr_t)-1; 488 | 489 | src_params = &shbskt->src_params; 490 | memcpy(&str_hub->src_conn_params, src_conn_params, sizeof(str_src_conn_params_t)); 491 | conn_udp = &src_conn_params->udp; 492 | error = skt_bind(&conn_udp->addr, SOCK_DGRAM, IPPROTO_UDP, 493 | (SO_F_NONBLOCK | SO_F_REUSEADDR | SO_F_REUSEPORT), 494 | &skt); 495 | if (0 != error) /* Bind to mc addr fail, try bind inaddr_any. */ 496 | error = skt_bind_ap(conn_udp->addr.ss_family, 497 | NULL, sa_port_get(&conn_udp->addr), 498 | SOCK_DGRAM, IPPROTO_UDP, 499 | (SO_F_NONBLOCK | SO_F_REUSEADDR | SO_F_REUSEPORT), 500 | &skt); 501 | if (0 != error) { 502 | skt = (uintptr_t)-1; 503 | SYSLOG_ERR(LOG_ERR, error, "skt_bind_ap()."); 504 | goto err_out; 505 | } 506 | /* Join to multicast group. */ 507 | error = skt_mc_join(skt, 1, src_conn_params->mc.if_index, 508 | &conn_udp->addr); 509 | if (0 != error) { 510 | SYSLOG_ERR(LOG_ERR, error, "skt_mc_join()."); 511 | goto err_out; 512 | } 513 | /* Tune socket. */ 514 | error = skt_rcv_tune(skt, src_params->skt_rcv_buf, src_params->skt_rcv_lowat); 515 | if (0 != error) { 516 | SYSLOG_ERR(LOG_ERR, error, "skt_rcv_tune()."); 517 | goto err_out; 518 | } 519 | /* Create IO task for socket. */ 520 | error = tp_task_notify_create(str_hub->tpt, skt, 521 | TP_TASK_F_CLOSE_ON_DESTROY, TP_EV_READ, 0, str_src_recv_mc_cb, 522 | str_hub, &str_hub->tptask); 523 | if (0 != error) { 524 | SYSLOG_ERR(LOG_ERR, error, "tp_task_notify_create()."); 525 | goto err_out; 526 | } 527 | 528 | TAILQ_INSERT_HEAD(&shbskt->thr_data[tpt_get_num(tpt)].hub_head, 529 | str_hub, next); 530 | 531 | syslog(LOG_INFO, "%s: Created. (fd: %zu)", str_hub->name, skt); 532 | 533 | (*str_hub_ret) = str_hub; 534 | return (0); 535 | 536 | err_out: 537 | /* Error. */ 538 | close((int)skt); 539 | free(str_hub); 540 | (*str_hub_ret) = NULL; 541 | SYSLOG_ERR_EX(LOG_ERR, error, "..."); 542 | return (error); 543 | } 544 | 545 | void 546 | str_hub_destroy_int(str_hub_p str_hub) { 547 | str_hub_cli_p strh_cli, strh_cli_temp; 548 | 549 | SYSLOGD_EX(LOG_DEBUG, "..."); 550 | 551 | if (NULL == str_hub) 552 | return; 553 | /* Leave multicast group. */ 554 | tp_task_destroy(str_hub->tptask); 555 | 556 | if (TAILQ_PREV_PTR(str_hub, next)) { 557 | TAILQ_REMOVE(&str_hub->shbskt->thr_data[tpt_get_num(str_hub->tpt)].hub_head, 558 | str_hub, next); 559 | } 560 | /* Destroy all connected clients. */ 561 | TAILQ_FOREACH_SAFE(strh_cli, &str_hub->cli_head, next, strh_cli_temp) { 562 | str_hub_cli_destroy(str_hub, strh_cli); 563 | } 564 | 565 | syslog(LOG_INFO, "%s: Destroyed.", str_hub->name); 566 | 567 | str_src_r_buf_free(str_hub); 568 | free(str_hub); 569 | } 570 | 571 | 572 | str_hub_cli_p 573 | str_hub_cli_alloc(uintptr_t skt, const char *ua, size_t ua_size) { 574 | str_hub_cli_p strh_cli; 575 | 576 | SYSLOGD_EX(LOG_DEBUG, "..."); 577 | 578 | if (STR_HUB_CLI_USER_AGENT_MAX_SIZE < ua_size) 579 | ua_size = STR_HUB_CLI_USER_AGENT_MAX_SIZE; 580 | strh_cli = calloc(1, sizeof(str_hub_cli_t) + ua_size + sizeof(void*)); 581 | if (NULL == strh_cli) 582 | return (NULL); 583 | /* Set. */ 584 | strh_cli->skt = skt; 585 | strh_cli->user_agent = (uint8_t*)(strh_cli + 1); 586 | if (NULL != ua && 0 != ua_size) { 587 | strh_cli->user_agent_size = ua_size; 588 | memcpy(strh_cli->user_agent, ua, ua_size); 589 | strh_cli->user_agent[ua_size] = 0; 590 | } 591 | 592 | return (strh_cli); 593 | } 594 | 595 | void 596 | str_hub_cli_destroy(str_hub_p str_hub, str_hub_cli_p strh_cli) { 597 | char straddr[STR_ADDR_LEN]; 598 | struct msghdr mhdr; 599 | struct iovec iov[4]; 600 | 601 | SYSLOGD_EX(LOG_DEBUG, "..."); 602 | 603 | if (NULL == strh_cli) 604 | return; 605 | if (NULL != str_hub) { 606 | sa_addr_port_to_str(&strh_cli->remonte_addr, straddr, 607 | sizeof(straddr), NULL); 608 | syslog(LOG_INFO, "%s - %s: deattached, cli_count = %zu", 609 | str_hub->name, straddr, (str_hub->cli_count - 1)); 610 | /* Remove from stream hub. */ 611 | TAILQ_REMOVE(&str_hub->cli_head, strh_cli, next); 612 | str_hub->cli_count --; 613 | } 614 | 615 | /* Send HTTP headers if needed. */ 616 | if (0 == (STR_HUB_CLI_STATE_F_HTTP_HDRS_SENDED & strh_cli->flags) && 617 | 0 == strh_cli->offset) { 618 | memset(&mhdr, 0x00, sizeof(mhdr)); 619 | mhdr.msg_iov = (struct iovec*)iov; 620 | 621 | iov[mhdr.msg_iovlen].iov_base = MK_RW_PTR("HTTP/1.1 503 Service Unavailable\r\n"); 622 | iov[mhdr.msg_iovlen].iov_len = 34; 623 | mhdr.msg_iovlen ++; 624 | if (NULL != str_hub) { 625 | iov[mhdr.msg_iovlen].iov_base = str_hub->shbskt->base_http_hdrs; 626 | iov[mhdr.msg_iovlen].iov_len = str_hub->shbskt->base_http_hdrs_size; 627 | mhdr.msg_iovlen ++; 628 | } 629 | iov[mhdr.msg_iovlen].iov_base = MK_RW_PTR("\r\n"); 630 | iov[mhdr.msg_iovlen].iov_len = 2; 631 | mhdr.msg_iovlen ++; 632 | sendmsg((int)strh_cli->skt, &mhdr, (MSG_DONTWAIT | MSG_NOSIGNAL)); 633 | } 634 | 635 | close((int)strh_cli->skt); 636 | free(strh_cli); 637 | } 638 | 639 | 640 | int 641 | str_hub_cli_attach(str_hubs_bckt_p shbskt, str_hub_cli_p strh_cli, 642 | uint8_t *hub_name, size_t hub_name_size, str_src_conn_params_p src_conn_params) { 643 | int error; 644 | tpt_p tpt; 645 | str_hub_cli_attach_cb_data_p cli_data; 646 | 647 | if (NULL == shbskt || NULL == strh_cli || NULL == hub_name || 648 | 0 == hub_name_size || NULL == src_conn_params) 649 | return (EINVAL); 650 | cli_data = calloc(1, sizeof(str_hub_cli_attach_cb_data_t) + hub_name_size + sizeof(void*)); 651 | if (NULL == cli_data) 652 | return (ENOMEM); 653 | cli_data->shbskt = shbskt; 654 | cli_data->strh_cli = strh_cli; 655 | cli_data->hub_name = (uint8_t*)(cli_data + 1); 656 | memcpy(cli_data->hub_name, hub_name, hub_name_size); 657 | cli_data->hub_name[hub_name_size] = 0; 658 | cli_data->hub_name_size = hub_name_size; 659 | memcpy(&cli_data->src_conn_params, src_conn_params, sizeof(str_src_conn_params_t)); 660 | 661 | tpt = str_hub_tpt_get_by_name(shbskt->tp, hub_name, hub_name_size); 662 | error = tpt_msg_send(tpt, NULL, TP_MSG_F_SELF_DIRECT, 663 | str_hub_cli_attach_msg_cb, cli_data); 664 | if (0 != error) { 665 | free(cli_data); 666 | } 667 | 668 | return (error); 669 | } 670 | void 671 | str_hub_cli_attach_msg_cb(tpt_p tpt, void *udata) { 672 | str_hub_cli_attach_cb_data_p cli_data = udata; 673 | str_hub_p str_hub, str_hub_temp; 674 | str_hub_cli_p strh_cli; 675 | str_hub_settings_p hub_params; 676 | char straddr[STR_ADDR_LEN]; 677 | size_t thread_num; 678 | int error = -1; 679 | 680 | SYSLOGD_EX(LOG_DEBUG, "..."); 681 | 682 | thread_num = tpt_get_num(tpt); 683 | TAILQ_FOREACH_SAFE(str_hub, &cli_data->shbskt->thr_data[thread_num].hub_head, 684 | next, str_hub_temp) { 685 | if (str_hub->name_size != cli_data->hub_name_size) 686 | continue; 687 | if (0 == memcmp(str_hub->name, cli_data->hub_name, cli_data->hub_name_size)) { 688 | error = 0; 689 | break; 690 | } 691 | } 692 | if (0 != error) { /* Create new... */ 693 | error = str_hub_create_int(cli_data->shbskt, tpt, 694 | cli_data->hub_name, cli_data->hub_name_size, 695 | &cli_data->src_conn_params, &str_hub); 696 | if (0 != error) { 697 | str_hub_cli_destroy(NULL, cli_data->strh_cli); 698 | close((int)cli_data->strh_cli->skt); 699 | free(cli_data); 700 | SYSLOG_ERR(LOG_ERR, error, "str_hub_create()."); 701 | return; 702 | } 703 | } 704 | 705 | strh_cli = cli_data->strh_cli; 706 | hub_params = &cli_data->shbskt->hub_params; 707 | 708 | sa_addr_port_to_str(&strh_cli->remonte_addr, straddr, sizeof(straddr), NULL); 709 | 710 | /* Set. */ 711 | strh_cli->conn_time = gettime_monotonic(); 712 | /* Tune socket. */ 713 | /* Reduce kernel memory usage. */ 714 | error = skt_rcv_tune(strh_cli->skt, STR_HUB_CLI_RECV_BUF, STR_HUB_CLI_RECV_LOWAT); 715 | SYSLOG_ERR(LOG_NOTICE, error, "%s - %s: skt_rcv_tune().", 716 | str_hub->name, straddr); 717 | error = skt_snd_tune(strh_cli->skt, hub_params->skt_snd_buf, 1); 718 | SYSLOG_ERR(LOG_NOTICE, error, "%s - %s: skt_snd_tune().", 719 | str_hub->name, straddr); 720 | if (0 != (STR_HUB_S_F_SKT_HALFCLOSED & hub_params->flags)) { 721 | if (0 != shutdown((int)strh_cli->skt, SHUT_RD)) { 722 | error = errno; 723 | SYSLOG_ERR(LOG_NOTICE, error, "%s - %s: shutdown(..., SHUT_RD).", 724 | str_hub->name, straddr); 725 | } 726 | } 727 | error = skt_set_tcp_nodelay(strh_cli->skt, (STR_HUB_S_F_SKT_TCP_NODELAY & hub_params->flags)); 728 | SYSLOG_ERR(LOG_NOTICE, error, "%s - %s: skt_set_tcp_nodelay().", 729 | str_hub->name, straddr); 730 | error = skt_set_tcp_nopush(strh_cli->skt, (STR_HUB_S_F_SKT_TCP_NOPUSH & hub_params->flags)); 731 | SYSLOG_ERR(LOG_NOTICE, error, "%s - %s: skt_set_tcp_nopush().", 732 | str_hub->name, straddr); 733 | if (0 != hub_params->cc_name_size) { 734 | error = skt_set_tcp_cc(strh_cli->skt, hub_params->cc_name, 735 | hub_params->cc_name_size); 736 | SYSLOG_ERR(LOG_NOTICE, error, "%s - %s: skt_set_tcp_cc().", 737 | str_hub->name, straddr); 738 | } 739 | 740 | syslog(LOG_INFO, "%s - %s: attached, cli_count = %zu", 741 | str_hub->name, straddr, (str_hub->cli_count + 1)); 742 | 743 | TAILQ_INSERT_HEAD(&str_hub->cli_head, strh_cli, next); 744 | str_hub->cli_count ++; 745 | free(cli_data); 746 | } 747 | 748 | 749 | 750 | int 751 | str_hub_send_to_client(str_hub_p str_hub, str_hub_cli_p strh_cli, 752 | size_t *transfered_size) { 753 | int error = 0; 754 | off_t sbytes = 0; 755 | size_t data2send, i, iov_cnt, drop_size, tr_size = 0; 756 | struct iovec iov[4]; 757 | 758 | /* Get data avail for client. */ 759 | data2send = r_buf_data_avail_size(str_hub->r_buf, &strh_cli->rpos, &drop_size); 760 | if (str_hub->shbskt->hub_params.snd_block_min_size > data2send) 761 | return (0); /* Not enough data for this client. */ 762 | if (data2send > str_hub->shbskt->hub_params.skt_snd_buf) { 763 | data2send = str_hub->shbskt->hub_params.skt_snd_buf; 764 | } 765 | iov_cnt = r_buf_data_get(str_hub->r_buf, &strh_cli->rpos, data2send, 766 | (iovec_p)iov, 4, &drop_size, NULL); 767 | if (0 == iov_cnt) { /* Nothink to send? */ 768 | if (0 != drop_size) 769 | error = -1; 770 | goto err_out; 771 | } 772 | /* Send. */ 773 | r_buf_data_get_conv2off(str_hub->r_buf, (iovec_p)iov, iov_cnt); 774 | for (i = 0; i < iov_cnt; i ++) { 775 | error = skt_sendfile(str_hub->r_buf_fd, strh_cli->skt, 776 | (off_t)iov[i].iov_base, iov[i].iov_len, 777 | (SKT_SF_F_NODISKIO), &sbytes); 778 | tr_size += (size_t)sbytes; 779 | if (0 != error) 780 | break; 781 | } 782 | /* Supress some errors. */ 783 | error = SKT_ERR_FILTER(error); 784 | /* Update client read pos. */ 785 | r_buf_rpos_inc(str_hub->r_buf, &strh_cli->rpos, tr_size); 786 | 787 | err_out: 788 | if (NULL != transfered_size) { 789 | (*transfered_size) = tr_size; 790 | } 791 | 792 | return (error); 793 | } 794 | 795 | int 796 | str_hub_send_to_clients(str_hub_p str_hub) { 797 | int error; 798 | str_hub_cli_p strh_cli, strh_cli_temp; 799 | struct msghdr mhdr; 800 | struct iovec iov[4]; 801 | ssize_t ios; 802 | size_t transfered_size; 803 | char straddr[STR_ADDR_LEN]; 804 | 805 | TAILQ_FOREACH_SAFE(strh_cli, &str_hub->cli_head, next, strh_cli_temp) { 806 | transfered_size = 0; 807 | /* Send HTTP headers if needed. */ 808 | if (0 == (STR_HUB_CLI_STATE_F_HTTP_HDRS_SENDED & strh_cli->flags)) { 809 | memset(&mhdr, 0x00, sizeof(mhdr)); 810 | mhdr.msg_iov = (struct iovec*)iov; 811 | mhdr.msg_iovlen = 3; 812 | iov[0].iov_base = MK_RW_PTR("HTTP/1.1 200 OK\r\n"); 813 | iov[0].iov_len = 17; 814 | iov[1].iov_base = str_hub->shbskt->base_http_hdrs; 815 | iov[1].iov_len = str_hub->shbskt->base_http_hdrs_size; 816 | iov[2].iov_base = str_hub->shbskt->hub_params.cust_http_hdrs; 817 | iov[2].iov_len = str_hub->shbskt->hub_params.cust_http_hdrs_size; 818 | /* Skip allready sended data. */ 819 | iovec_set_offset(mhdr.msg_iov, (size_t)mhdr.msg_iovlen, strh_cli->offset); 820 | ios = sendmsg((int)strh_cli->skt, &mhdr, (MSG_DONTWAIT | MSG_NOSIGNAL)); 821 | if (-1 == ios) { /* Error happen. */ 822 | /* Supress some errors. */ 823 | error = SKT_ERR_FILTER(errno); 824 | goto error_on_send; 825 | } 826 | SYSLOGD_EX(LOG_DEBUG, "HTTP hdr: %zu", ios); 827 | strh_cli->offset += (size_t)ios; 828 | if (iovec_calc_size(mhdr.msg_iov, (size_t)mhdr.msg_iovlen) > 829 | (size_t)ios) /* Not all HTTP headers sended. */ 830 | continue; /* Try to send next headers part later. */ 831 | strh_cli->offset = 0; 832 | strh_cli->flags |= STR_HUB_CLI_STATE_F_HTTP_HDRS_SENDED; 833 | } 834 | /* Init uninitialized client rpos. */ 835 | if (0 == (STR_HUB_CLI_STATE_F_RPOS_INITIALIZED & strh_cli->flags)) { 836 | strh_cli->flags |= STR_HUB_CLI_STATE_F_RPOS_INITIALIZED; 837 | r_buf_rpos_init(str_hub->r_buf, &strh_cli->rpos, 838 | str_hub->shbskt->hub_params.precache); 839 | } 840 | error = str_hub_send_to_client(str_hub, strh_cli, &transfered_size); 841 | error_on_send: 842 | if (0 != error) { 843 | sa_addr_port_to_str(&strh_cli->remonte_addr, straddr, sizeof(straddr), NULL); 844 | SYSLOG_ERR(LOG_ERR, error, "%s - %s: disconnected.", 845 | str_hub->name, straddr); 846 | if (-1 != error || 847 | 0 != (STR_HUB_S_F_DROP_SLOW_CLI & str_hub->shbskt->hub_params.flags)) 848 | str_hub_cli_destroy(str_hub, strh_cli); 849 | continue; 850 | } 851 | str_hub->sended_count += transfered_size; 852 | } 853 | 854 | return (0); 855 | } 856 | 857 | 858 | /* MPEG payload-type constants - adopted from VLC 0.8.6 */ 859 | #define P_MPGA 0x0E /* MPEG audio */ 860 | #define P_MPGV 0x20 /* MPEG video */ 861 | 862 | static int 863 | str_src_recv_mc_cb(tp_task_p tptask, int error, uint32_t eof __unused, 864 | size_t data2transfer_size, void *arg) { 865 | str_hub_p str_hub = arg; 866 | uintptr_t ident; 867 | ssize_t ios; 868 | uint8_t *buf; 869 | size_t transfered_size = 0, req_buf_size, buf_size, start_off = 0, end_off = 0; 870 | 871 | if (0 != error) { 872 | err_out: 873 | SYSLOG_ERR(LOG_DEBUG, error, "On receive."); 874 | str_hub_destroy_int(str_hub); 875 | return (TP_TASK_CB_NONE); /* Receiver destroyed. */ 876 | } 877 | if (NULL == str_hub->r_buf) { /* Delay ring buf allocation. */ 878 | error = str_src_r_buf_alloc(str_hub); 879 | if (0 != error) 880 | goto err_out; 881 | } 882 | 883 | ident = tp_task_ident_get(tptask); 884 | req_buf_size = STR_SRC_UDP_PKT_SIZE_STD; 885 | while (transfered_size < data2transfer_size) { /* recv loop. */ 886 | buf_size = r_buf_wbuf_get(str_hub->r_buf, req_buf_size, &buf); 887 | ios = recv((int)ident, buf, buf_size, MSG_DONTWAIT); 888 | if (-1 == ios) { 889 | error = errno; 890 | if (0 == error) 891 | error = EINVAL; 892 | error = SKT_ERR_FILTER(error); 893 | if (0 == error && STR_SRC_UDP_PKT_SIZE_MAX > buf_size) { 894 | /* Possible not enough buf space. */ 895 | req_buf_size = STR_SRC_UDP_PKT_SIZE_MAX; 896 | continue; /* Retry! */ 897 | } 898 | break; 899 | } 900 | if (0 == ios) 901 | break; 902 | transfered_size += (size_t)ios; 903 | if (MPEG2_TS_PKT_SIZE_MIN > (size_t)ios) 904 | continue; /* Packet to small, drop. */ 905 | if (MPEG2_TS_HDR_IS_VALID((mpeg2_ts_hdr_p)buf)) { /* Test_ for RTP. */ 906 | buf_size = (size_t)ios; 907 | } else if (0 == rtp_payload_get(buf, (size_t)ios, &start_off, &end_off)) { 908 | /* XXX skip payload bulk data. */ 909 | if (P_MPGA == ((rtp_hdr_p)buf)->pt || 910 | P_MPGV == ((rtp_hdr_p)buf)->pt) 911 | start_off += 4; 912 | buf_size = ((size_t)ios - (start_off + end_off)); 913 | if (MPEG2_TS_PKT_SIZE_MIN > buf_size) 914 | continue; /* Packet to small, drop. */ 915 | /* Prevent fragmentation, zero move: buf += start_off; */ 916 | memmove(buf, (buf + start_off), buf_size); 917 | } else { 918 | continue; /* Packet unknown, drop. */ 919 | } 920 | r_buf_wbuf_set2(str_hub->r_buf, buf, buf_size, NULL); 921 | } /* end recv while */ 922 | if (0 != error) { 923 | SYSLOG_ERR(LOG_NOTICE, error, "recv()."); 924 | if (0 == transfered_size) 925 | goto rcv_next; 926 | } 927 | /* Calc speed. */ 928 | str_hub->received_count += transfered_size; 929 | clock_gettime(CLOCK_MONOTONIC_FAST, &str_hub->tp_last_recv); 930 | 931 | #ifdef __linux__ /* Linux specific code. */ 932 | /* Ring buf LOWAT emulator. */ 933 | str_hub->r_buf_rcvd += transfered_size; 934 | if (str_hub->r_buf_rcvd < str_hub->shbskt->src_params.skt_rcv_lowat) 935 | goto rcv_next; 936 | str_hub->r_buf_rcvd = 0; 937 | #endif /* Linux specific code. */ 938 | str_hub_send_to_clients(str_hub); 939 | 940 | rcv_next: 941 | return (TP_TASK_CB_CONTINUE); 942 | } 943 | 944 | 945 | int 946 | str_src_r_buf_alloc(str_hub_p str_hub) { 947 | int error; 948 | char hash[(MD5_HASH_STR_SIZE + 1)], filename[128]; 949 | struct timespec tv_now; 950 | 951 | /* Create buf */ 952 | clock_gettime(CLOCK_MONOTONIC_FAST, &tv_now); 953 | md5_get_digest_str((char*)&tv_now, sizeof(tv_now), (char*)hash); 954 | snprintf(filename, sizeof(filename), "/tmp/msd-%zu-%s.tmp", 955 | (size_t)getpid(), hash); 956 | str_hub->r_buf_fd = (uintptr_t)open(filename, (O_CREAT | O_EXCL | O_RDWR), 0600); 957 | if ((uintptr_t)-1 == str_hub->r_buf_fd) { 958 | error = errno; 959 | SYSLOG_ERR(LOG_ERR, error, "open(%s).", filename); 960 | goto err_out; 961 | } 962 | if (0 != flock((int)str_hub->r_buf_fd, LOCK_EX)) { 963 | SYSLOG_ERR(LOG_NOTICE, errno, "flock(%s).", filename); 964 | } 965 | 966 | /* Truncate it to the correct size */ 967 | if (0 != ftruncate((int)str_hub->r_buf_fd, (off_t)str_hub->shbskt->hub_params.ring_buf_size)) { 968 | error = errno; 969 | SYSLOG_ERR(LOG_ERR, error, "ftruncate(%s).", filename); 970 | goto err_out; 971 | } 972 | str_hub->r_buf = r_buf_alloc(str_hub->r_buf_fd, 973 | str_hub->shbskt->hub_params.ring_buf_size, 974 | MPEG2_TS_PKT_SIZE_188, 0); 975 | if (NULL == str_hub->r_buf) { 976 | error = errno; 977 | SYSLOG_ERR(LOG_ERR, error, "r_buf_alloc()."); 978 | goto err_out; 979 | } 980 | unlink(filename); 981 | 982 | return (0); 983 | 984 | err_out: 985 | /* Error. */ 986 | flock((int)str_hub->r_buf_fd, LOCK_UN); 987 | close((int)str_hub->r_buf_fd); 988 | unlink(filename); 989 | SYSLOG_ERR_EX(LOG_ERR, error, "..."); 990 | return (error); 991 | } 992 | 993 | void 994 | str_src_r_buf_free(str_hub_p str_hub) { 995 | 996 | if (NULL == str_hub) 997 | return; 998 | flock((int)str_hub->r_buf_fd, LOCK_UN); 999 | close((int)str_hub->r_buf_fd); 1000 | str_hub->r_buf_fd = (uintptr_t)-1; 1001 | r_buf_free(str_hub->r_buf); 1002 | str_hub->r_buf = NULL; 1003 | } 1004 | --------------------------------------------------------------------------------