build: add freebsd support (#4049)

This commit is contained in:
David Lane 2025-11-11 23:46:11 -05:00 committed by GitHub
commit 1d6d916b7a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
31 changed files with 1055 additions and 39 deletions

View file

@ -10,14 +10,22 @@
// standard includes
#include <fstream>
#include <iomanip>
#include <iostream>
#include <sstream>
// platform includes
#include <arpa/inet.h>
#include <dlfcn.h>
#include <ifaddrs.h>
#include <netinet/in.h>
#include <netinet/udp.h>
#include <pwd.h>
#include <sys/socket.h>
#ifdef __FreeBSD__
#include <net/if_dl.h> // For sockaddr_dl, LLADDR, and AF_LINK
#endif
// lib includes
#include <boost/asio/ip/address.hpp>
@ -41,6 +49,16 @@
#define SUNSHINE_GNUC_EXTENSION
#endif
#ifndef SOL_IP
#define SOL_IP IPPROTO_IP
#endif
#ifndef SOL_IPV6
#define SOL_IPV6 IPPROTO_IPV6
#endif
#ifndef SOL_UDP
#define SOL_UDP IPPROTO_UDP
#endif
using namespace std::literals;
namespace fs = std::filesystem;
namespace bp = boost::process::v1;
@ -214,6 +232,40 @@ namespace platf {
std::string get_mac_address(const std::string_view &address) {
auto ifaddrs = get_ifaddrs();
#ifdef __FreeBSD__
// On FreeBSD, we need to find the interface name first, then look for its AF_LINK entry
std::string interface_name;
for (auto pos = ifaddrs.get(); pos != nullptr; pos = pos->ifa_next) {
if (pos->ifa_addr && address == from_sockaddr(pos->ifa_addr)) {
interface_name = pos->ifa_name;
break;
}
}
if (!interface_name.empty()) {
// Find the AF_LINK entry for this interface to get MAC address
for (auto pos = ifaddrs.get(); pos != nullptr; pos = pos->ifa_next) {
if (pos->ifa_addr && pos->ifa_addr->sa_family == AF_LINK &&
interface_name == pos->ifa_name) {
auto sdl = (struct sockaddr_dl *) pos->ifa_addr;
auto mac = (unsigned char *) LLADDR(sdl);
// Format MAC address as XX:XX:XX:XX:XX:XX
std::ostringstream mac_stream;
mac_stream << std::hex << std::setfill('0');
for (int i = 0; i < sdl->sdl_alen; i++) {
if (i > 0) {
mac_stream << ':';
}
mac_stream << std::setw(2) << (int) mac[i];
}
return mac_stream.str();
}
}
}
#else
// On Linux, read MAC address from sysfs
for (auto pos = ifaddrs.get(); pos != nullptr; pos = pos->ifa_next) {
if (pos->ifa_addr && address == from_sockaddr(pos->ifa_addr)) {
std::ifstream mac_file("/sys/class/net/"s + pos->ifa_name + "/address");
@ -224,6 +276,7 @@ namespace platf {
}
}
}
#endif
BOOST_LOG(warning) << "Unable to find MAC address for "sv << address;
return "00:00:00:00:00:00"s;
@ -377,7 +430,12 @@ namespace platf {
}
union {
#ifdef IP_PKTINFO
char buf[CMSG_SPACE(sizeof(uint16_t)) + std::max(CMSG_SPACE(sizeof(struct in_pktinfo)), CMSG_SPACE(sizeof(struct in6_pktinfo)))];
#elif defined(IP_SENDSRCADDR)
// FreeBSD uses IP_SENDSRCADDR with struct in_addr instead of IP_PKTINFO with struct in_pktinfo
char buf[CMSG_SPACE(sizeof(uint16_t)) + std::max(CMSG_SPACE(sizeof(struct in_addr)), CMSG_SPACE(sizeof(struct in6_pktinfo)))];
#endif
struct cmsghdr alignment;
} cmbuf = {}; // Must be zeroed for CMSG_NXTHDR()
@ -403,6 +461,7 @@ namespace platf {
pktinfo_cm->cmsg_len = CMSG_LEN(sizeof(pktInfo));
memcpy(CMSG_DATA(pktinfo_cm), &pktInfo, sizeof(pktInfo));
} else {
#ifdef IP_PKTINFO
struct in_pktinfo pktInfo;
struct sockaddr_in saddr_v4 = to_sockaddr(send_info.source_address.to_v4(), 0);
@ -415,6 +474,18 @@ namespace platf {
pktinfo_cm->cmsg_type = IP_PKTINFO;
pktinfo_cm->cmsg_len = CMSG_LEN(sizeof(pktInfo));
memcpy(CMSG_DATA(pktinfo_cm), &pktInfo, sizeof(pktInfo));
#elif defined(IP_SENDSRCADDR)
// FreeBSD uses IP_SENDSRCADDR with struct in_addr instead of IP_PKTINFO
struct sockaddr_in saddr_v4 = to_sockaddr(send_info.source_address.to_v4(), 0);
struct in_addr src_addr = saddr_v4.sin_addr;
cmbuflen += CMSG_SPACE(sizeof(src_addr));
pktinfo_cm->cmsg_level = IPPROTO_IP;
pktinfo_cm->cmsg_type = IP_SENDSRCADDR;
pktinfo_cm->cmsg_len = CMSG_LEN(sizeof(src_addr));
memcpy(CMSG_DATA(pktinfo_cm), &src_addr, sizeof(src_addr));
#endif
}
auto const max_iovs_per_msg = send_info.payload_buffers.size() + (send_info.headers ? 1 : 0);
@ -507,8 +578,8 @@ namespace platf {
{
// If GSO is not supported, use sendmmsg() instead.
struct mmsghdr msgs[send_info.block_count];
struct iovec iovs[send_info.block_count * (send_info.headers ? 2 : 1)];
std::vector<struct mmsghdr> msgs(send_info.block_count);
std::vector<struct iovec> iovs(send_info.block_count * (send_info.headers ? 2 : 1));
int iov_idx = 0;
for (size_t i = 0; i < send_info.block_count; i++) {
msgs[i].msg_len = 0;
@ -584,7 +655,12 @@ namespace platf {
}
union {
#ifdef IP_PKTINFO
char buf[std::max(CMSG_SPACE(sizeof(struct in_pktinfo)), CMSG_SPACE(sizeof(struct in6_pktinfo)))];
#elif defined(IP_SENDSRCADDR)
// FreeBSD uses IP_SENDSRCADDR with struct in_addr instead of IP_PKTINFO with struct in_pktinfo
char buf[std::max(CMSG_SPACE(sizeof(struct in_addr)), CMSG_SPACE(sizeof(struct in6_pktinfo)))];
#endif
struct cmsghdr alignment;
} cmbuf;
@ -608,6 +684,7 @@ namespace platf {
pktinfo_cm->cmsg_len = CMSG_LEN(sizeof(pktInfo));
memcpy(CMSG_DATA(pktinfo_cm), &pktInfo, sizeof(pktInfo));
} else {
#ifdef IP_PKTINFO
struct in_pktinfo pktInfo;
struct sockaddr_in saddr_v4 = to_sockaddr(send_info.source_address.to_v4(), 0);
@ -620,6 +697,18 @@ namespace platf {
pktinfo_cm->cmsg_type = IP_PKTINFO;
pktinfo_cm->cmsg_len = CMSG_LEN(sizeof(pktInfo));
memcpy(CMSG_DATA(pktinfo_cm), &pktInfo, sizeof(pktInfo));
#elif defined(IP_SENDSRCADDR)
// FreeBSD uses IP_SENDSRCADDR with struct in_addr instead of IP_PKTINFO
struct sockaddr_in saddr_v4 = to_sockaddr(send_info.source_address.to_v4(), 0);
struct in_addr src_addr = saddr_v4.sin_addr;
cmbuflen += CMSG_SPACE(sizeof(src_addr));
pktinfo_cm->cmsg_level = IPPROTO_IP;
pktinfo_cm->cmsg_type = IP_SENDSRCADDR;
pktinfo_cm->cmsg_len = CMSG_LEN(sizeof(src_addr));
memcpy(CMSG_DATA(pktinfo_cm), &src_addr, sizeof(src_addr));
#endif
}
struct iovec iovs[2];
@ -753,6 +842,10 @@ namespace platf {
// reset SO_PRIORITY back to 0.
//
// 6 is the highest priority that can be used without SYS_CAP_ADMIN.
#ifndef SO_PRIORITY
// FreeBSD doesn't support SO_PRIORITY, so we skip this
BOOST_LOG(debug) << "SO_PRIORITY not supported on this platform, skipping traffic priority setting";
#else
int priority = data_type == qos_data_type_e::audio ? 6 : 5;
if (setsockopt(sockfd, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)) == 0) {
// Reset SO_PRIORITY to 0 when QoS is disabled
@ -760,6 +853,7 @@ namespace platf {
} else {
BOOST_LOG(error) << "Failed to set SO_PRIORITY: "sv << errno;
}
#endif
return std::make_unique<qos_t>(sockfd, reset_options);
}

View file

@ -520,7 +520,12 @@ namespace stream {
// for other communications to the client. This is necessary to ensure
// proper routing on multi-homed hosts.
auto local_address = platf::from_sockaddr((sockaddr *) &peer->localAddress.address);
session_p->localAddress = boost::asio::ip::make_address(local_address);
try {
session_p->localAddress = boost::asio::ip::make_address(local_address);
} catch (const boost::system::system_error &e) {
BOOST_LOG(error) << "boost::system::system_error in address parsing: " << e.what() << " (code: " << e.code() << ")"sv;
throw;
}
BOOST_LOG(debug) << "Control local address ["sv << local_address << ']';
BOOST_LOG(debug) << "Control peer address ["sv << peer_addr << ':' << peer_port << ']';

View file

@ -13,7 +13,7 @@
#define TRAY_ICON_PLAYING WEB_DIR "images/sunshine-playing.ico"
#define TRAY_ICON_PAUSING WEB_DIR "images/sunshine-pausing.ico"
#define TRAY_ICON_LOCKED WEB_DIR "images/sunshine-locked.ico"
#elif defined(__linux__) || defined(linux) || defined(__linux)
#elif defined(__linux__) || defined(linux) || defined(__linux) || defined(__FreeBSD__)
#define TRAY_ICON SUNSHINE_TRAY_PREFIX "-tray"
#define TRAY_ICON_PLAYING SUNSHINE_TRAY_PREFIX "-playing"
#define TRAY_ICON_PAUSING SUNSHINE_TRAY_PREFIX "-pausing"

View file

@ -897,7 +897,7 @@ namespace video {
H264_ONLY | PARALLEL_ENCODING | ALWAYS_REPROBE | YUV444_SUPPORT
};
#ifdef __linux__
#if defined(__linux__) || defined(linux) || defined(__linux) || defined(__FreeBSD__)
encoder_t vaapi {
"vaapi"sv,
std::make_unique<encoder_platform_formats_avcodec>(
@ -1032,7 +1032,7 @@ namespace video {
&quicksync,
&amdvce,
#endif
#ifdef __linux__
#if defined(__linux__) || defined(linux) || defined(__linux) || defined(__FreeBSD__)
&vaapi,
#endif
#ifdef __APPLE__

View file

@ -222,7 +222,7 @@ namespace video {
extern encoder_t quicksync;
#endif
#ifdef __linux__
#if defined(__linux__) || defined(linux) || defined(__linux) || defined(__FreeBSD__)
extern encoder_t vaapi;
#endif