diff --git a/www/chromium/Makefile b/www/chromium/Makefile index 8a661c4204c9..9a83892f3789 100644 --- a/www/chromium/Makefile +++ b/www/chromium/Makefile @@ -1,432 +1,433 @@ PORTNAME= chromium PORTVERSION= 150.0.7871.46 +PORTREVISION= 1 PULSEMV= 16 PULSEV= ${PULSEMV}.1 CATEGORIES= www wayland MASTER_SITES= https://commondatastorage.googleapis.com/chromium-browser-official/ \ https://nerd.hu/distfiles/:external \ https://freedesktop.org/software/pulseaudio/releases/:pulseaudio DISTFILES= ${DISTNAME}-lite${EXTRACT_SUFX} \ pulseaudio-${PULSEV}.tar.gz:pulseaudio USE_GITHUB= nodefault GH_TUPLE= rollup:rollup:v4.60.4:rollup/rollup \ evanw:esbuild:v${_ESBUILD_VERSION:sh}:esbuild/esbuild MAINTAINER= chromium@FreeBSD.org COMMENT= Google web browser based on WebKit WWW= https://www.chromium.org/Home LICENSE= BSD3CLAUSE LGPL21 MPL11 LICENSE_COMB= multi ONLY_FOR_ARCHS= aarch64 amd64 BUILD_DEPENDS= bash:shells/bash \ ${PYTHON_PKGNAMEPREFIX}Jinja2>0:devel/py-Jinja2@${PY_FLAVOR} \ ${PYTHON_PKGNAMEPREFIX}ply>0:devel/py-ply@${PY_FLAVOR} \ bindgen:devel/rust-bindgen-cli \ esbuild:devel/esbuild \ gperf:devel/gperf \ flock:sysutils/flock \ node:www/node \ rustc:lang/rust \ xcb-proto>0:x11/xcb-proto \ ${LOCALBASE}/include/linux/videodev2.h:multimedia/v4l_compat \ ${LOCALBASE}/share/usbids/usb.ids:misc/usbids \ ${PYTHON_PKGNAMEPREFIX}html5lib>0:www/py-html5lib@${PY_FLAVOR} \ ${LOCALBASE}/include/va/va.h:multimedia/libva \ ${LOCALBASE}/libdata/pkgconfig/dri.pc:graphics/mesa-libs \ ${LOCALBASE}/libdata/pkgconfig/Qt5Core.pc:devel/qt5-core \ ${LOCALBASE}/libdata/pkgconfig/Qt5Widgets.pc:x11-toolkits/qt5-widgets LIB_DEPENDS= libatk-bridge-2.0.so:accessibility/at-spi2-core \ libatspi.so:accessibility/at-spi2-core \ libspeechd.so:accessibility/speech-dispatcher \ libFLAC.so:audio/flac \ libopus.so:audio/opus \ libspeex.so:audio/speex \ libdbus-1.so:devel/dbus \ libdbus-glib-1.so:devel/dbus-glib \ libepoll-shim.so:devel/libepoll-shim \ libevent.so:devel/libevent \ libffi.so:devel/libffi \ libicuuc.so:devel/icu \ libjsoncpp.so:devel/jsoncpp \ libpci.so:devel/libpci \ libnspr4.so:devel/nspr \ libre2.so:devel/re2 \ libcairo.so:graphics/cairo \ libdrm.so:graphics/libdrm \ libexif.so:graphics/libexif \ libpng.so:graphics/png \ libwebp.so:graphics/webp \ libdav1d.so:multimedia/dav1d \ libopenh264.so:multimedia/openh264 \ libfreetype.so:print/freetype2 \ libharfbuzz.so:print/harfbuzz \ libharfbuzz-icu.so:print/harfbuzz-icu \ libgcrypt.so:security/libgcrypt \ libsecret-1.so:security/libsecret \ libnss3.so:security/nss \ libexpat.so:textproc/expat2 \ libfontconfig.so:x11-fonts/fontconfig \ libwayland-client.so:graphics/wayland \ libxkbcommon.so:x11/libxkbcommon \ libxshmfence.so:x11/libxshmfence RUN_DEPENDS= xdg-open:devel/xdg-utils \ noto-basic>0:x11-fonts/noto-basic USES= bison cargo compiler:c++17-lang cpe desktop-file-utils gl gmake \ gnome iconv:wchar_t jpeg localbase:ldflags ninja perl5 pkgconfig \ python:build qt:5 shebangfix tar:xz xorg WITHOUT_FBSD10_FIX= Yes CARGO_CARGOTOML= ${WRKSRC}/rollup/rust/Cargo.toml CARGO_CARGOLOCK= ${WRKSRC}/rollup/rust/Cargo.lock CARGO_TARGET_DIR= ${WRKSRC}/rollup/rust/target CARGO_INSTALL= No CARGO_BUILD= No CARGO_TEST= No _ESBUILD_VERSION= ${MAKE} -C ${PORTSDIR}/devel/esbuild -V DISTVERSION CPE_VENDOR= google CPE_PRODUCT= chrome USE_GL= gbm gl USE_GNOME= atk dconf gdkpixbuf glib20 gtk30 libxml2 libxslt USE_LDCONFIG= ${DATADIR} USE_PERL5= build USE_XORG= x11 xcb xcomposite xcursor xext xdamage xfixes xi \ xorgproto xrandr xrender xscrnsaver xtst USE_QT= buildtools:build SHEBANG_FILES= chrome/tools/build/linux/chrome-wrapper #buildtools/linux64/clang-format MAKE_ARGS= -C out/${BUILDTYPE} ALL_TARGET= chrome BINARY_ALIAS= python3=${PYTHON_CMD} # TODO bz@ : install libwidevinecdm.so (see third_party/widevine/cdm/BUILD.gn) # # Run "./out/${BUILDTYPE}/gn args out/${BUILDTYPE} --list" for all variables. # Some parts don't have use_system_* flag, and can be turned on/off by using # replace_gn_files.py script, some parts just turned on/off for target host # OS "target_os == is_bsd", like libusb, libpci. GN_ARGS+= clang_use_chrome_plugins=false \ enable_backup_ref_ptr_support=false \ enable_hangout_services_extension=true \ enable_remoting=false \ enable_widevine=true \ fatal_linker_warnings=false \ icu_use_data_file=false \ is_clang=true \ moc_qt5_path="${QT_TOOLDIR}/moc" \ optimize_webui=true \ toolkit_views=true \ treat_warnings_as_errors=false \ use_allocator_shim=false \ use_aura=true \ use_custom_libcxx=true \ use_custom_libunwind=true \ use_lld=true \ use_partition_alloc=true \ use_partition_alloc_as_malloc=false \ use_qt5=true \ use_sysroot=false \ use_system_freetype=false \ use_system_harfbuzz=true \ use_system_libffi=true \ use_system_libjpeg=true \ use_udev=false \ extra_cxxflags="${CXXFLAGS} -I${LOCALBASE}/include/libepoll-shim ${PINCLUDES}" \ extra_ldflags="${LDFLAGS}" \ ffmpeg_branding="${FFMPEG_BRAND}" # TODO: investigate building with these options: # use_system_minigbm GN_BOOTSTRAP_FLAGS= --no-clean --no-rebuild --skip-generate-buildfiles # FreeBSD Chromium Api Key # Set up Google API keys, see http://www.chromium.org/developers/how-tos/api-keys . # Note: these are for FreeBSD use ONLY. For your own distribution, # please get your own set of keys. GN_ARGS+= google_api_key="AIzaSyBsp9n41JLW8jCokwn7vhoaMejDFRd1mp8" SUB_FILES= chromium-browser.desktop chrome SUB_LIST+= COMMENT="${COMMENT}" OPTIONS_DEFINE= CODECS CUPS DEBUG DRIVER KERBEROS LTO PIPEWIRE TEST WIDEVINE OPTIONS_DEFAULT= CODECS CUPS DRIVER KERBEROS PIPEWIRE SNDIO ALSA PULSEAUDIO OPTIONS_EXCLUDE_aarch64=LTO OPTIONS_GROUP= AUDIO OPTIONS_GROUP_AUDIO= ALSA PULSEAUDIO SNDIO OPTIONS_RADIO= KERBEROS OPTIONS_RADIO_KERBEROS= HEIMDAL HEIMDAL_BASE MIT OPTIONS_SUB= yes CODECS_DESC= Compile and enable patented codecs like H.264 DRIVER_DESC= Install chromedriver HEIMDAL_BASE_DESC= Heimdal Kerberos (base) HEIMDAL_DESC= Heimdal Kerberos (security/heimdal) MIT_DESC= MIT Kerberos (security/krb5) ALSA_LIB_DEPENDS= libasound.so:audio/alsa-lib ALSA_RUN_DEPENDS= ${LOCALBASE}/lib/alsa-lib/libasound_module_pcm_oss.so:audio/alsa-plugins \ alsa-lib>=1.1.1_1:audio/alsa-lib ALSA_VARS= GN_ARGS+=use_alsa=true ALSA_VARS_OFF= GN_ARGS+=use_alsa=false CODECS_VARS= GN_ARGS+=proprietary_codecs=true \ FFMPEG_BRAND="Chrome" CODECS_VARS_OFF= GN_ARGS+=proprietary_codecs=false \ FFMPEG_BRAND="Chromium" CUPS_LIB_DEPENDS= libcups.so:print/cups CUPS_VARS= GN_ARGS+=use_cups=true CUPS_VARS_OFF= GN_ARGS+=use_cups=false DEBUG_VARS= BUILDTYPE=Debug \ GN_ARGS+=is_debug=true \ GN_ARGS+=is_component_build=false \ GN_ARGS+=symbol_level=1 \ GN_BOOTSTRAP_FLAGS+=--debug \ WANTSPACE="60 GB" \ WANTMEM="32 GB" DEBUG_VARS_OFF= BUILDTYPE=Release \ GN_ARGS+=blink_symbol_level=0 \ GN_ARGS+=is_debug=false \ GN_ARGS+=is_official_build=true \ GN_ARGS+=symbol_level=0 \ WANTSPACE="35 GB" \ WANTMEM="8 GB" DRIVER_MAKE_ARGS= chromedriver HEIMDAL_LIB_DEPENDS= libkrb.so.26:security/heimdal KERBEROS_VARS= GN_ARGS+=use_kerberos=true KERBEROS_VARS_OFF= GN_ARGS+=use_kerberos=false LTO_VARS= GN_ARGS+=use_thin_lto=true \ GN_ARGS+=thin_lto_enable_optimizations=true \ WANTSPACE="40 GB" \ WANTMEM="16 GB" LTO_VARS_OFF= GN_ARGS+=use_thin_lto=false MIT_LIB_DEPENDS= libkrb.so.3:security/krb5 PIPEWIRE_DESC= Screen capture via PipeWire PIPEWIRE_LIB_DEPENDS= libpipewire-0.3.so:multimedia/pipewire PIPEWIRE_VARS= GN_ARGS+=rtc_use_pipewire=true \ GN_ARGS+=rtc_link_pipewire=true PIPEWIRE_VARS_OFF= GN_ARGS+=rtc_use_pipewire=false \ GN_ARGS+=rtc_link_pipewire=false PULSEAUDIO_VARS= GN_ARGS+=use_pulseaudio=true \ PINCLUDES="-I${WRKDIR}/pulseaudio-${PULSEV}/src -DPA_MAJOR=${PULSEMV}" PULSEAUDIO_VARS_OFF= GN_ARGS+=use_pulseaudio=false SNDIO_LIB_DEPENDS= libsndio.so:audio/sndio SNDIO_VARS= GN_ARGS+=use_sndio=true SNDIO_VARS_OFF= GN_ARGS+=use_sndio=false WIDEVINE_DESC= Depend on foreign-cdm to enable playback of DRM content WIDEVINE_RUN_DEPENDS= foreign-cdm>0:www/foreign-cdm .include "Makefile.tests" TEST_DISTFILES= ${PORTNAME}-${DISTVERSION}-testdata${EXTRACT_SUFX} TEST_ALL_TARGET= ${TEST_TARGETS} .include .include # for COMPILER_VERSION .if ${PORT_OPTIONS:MHEIMDAL_BASE} && !exists(/usr/lib/libkrb5.so) IGNORE= you have selected HEIMDAL_BASE but do not have Heimdal installed in base .endif LLVM_DEFAULT= 22 BUILD_DEPENDS+= clang${LLVM_DEFAULT}:devel/llvm${LLVM_DEFAULT} BINARY_ALIAS+= cpp=${LOCALBASE}/bin/clang-cpp${LLVM_DEFAULT} \ cc=${LOCALBASE}/bin/clang${LLVM_DEFAULT} \ c++=${LOCALBASE}/bin/clang++${LLVM_DEFAULT} \ ar=${LOCALBASE}/bin/llvm-ar${LLVM_DEFAULT} \ nm=${LOCALBASE}/bin/llvm-nm${LLVM_DEFAULT} \ ld=${LOCALBASE}/bin/ld.lld${LLVM_DEFAULT} # - swiftshader/lib/{libEGL.so,libGLESv2.so} is x86 only # - map ffmpeg target to ${ARCH} .if ${ARCH} == aarch64 PLIST_SUB+= NOT_AARCH64="@comment " FFMPEG_TARGET= arm64 .elif ${ARCH} == amd64 BUILD_DEPENDS+= nasm:devel/nasm PLIST_SUB+= NOT_AARCH64="" FFMPEG_TARGET= x64 .elif ${ARCH} == i386 BUILD_DEPENDS+= nasm:devel/nasm PLIST_SUB+= NOT_AARCH64="" FFMPEG_TARGET= ia32 .endif FFMPEG_BDIR= ${WRKSRC}/third_party/ffmpeg/build.${FFMPEG_TARGET}.freebsd/${FFMPEG_BRAND} FFMPEG_CDIR= ${WRKSRC}/third_party/ffmpeg/chromium/config/${FFMPEG_BRAND}/freebsd/${FFMPEG_TARGET} # Allow relocations against read-only segments (override lld default) LDFLAGS_i386= -Wl,-znotext # TODO: -isystem, would be just as ugly as this approach, but more reliably # build would fail without C_INCLUDE_PATH/CPLUS_INCLUDE_PATH env var set. MAKE_ENV+= C_INCLUDE_PATH=${LOCALBASE}/include \ CPLUS_INCLUDE_PATH=${LOCALBASE}/include # rust MAKE_ENV+= RUSTC_BOOTSTRAP=1 GN_ARGS+= enable_rust=true \ rust_sysroot_absolute="${LOCALBASE}" pre-everything:: @${ECHO_MSG} @${ECHO_MSG} "To build ${PORTNAME}, you should have at least:" @${ECHO_MSG} "- ${WANTMEM} of memory" @${ECHO_MSG} "- ${WANTSPACE} of free disk space" @${ECHO_MSG} "- 1.5 million free inodes" @${ECHO_MSG} pre-configure: # We used to remove bundled libraries to be sure that chromium uses # system libraries and not shipped ones. # cd ${WRKSRC} && ${PYTHON_CMD} \ #./build/linux/unbundle/remove_bundled_libraries.py [list of preserved] cd ${WRKSRC} && ${SETENV} ${CONFIGURE_ENV} ${PYTHON_CMD} \ ./build/linux/unbundle/replace_gn_files.py --system-libraries \ dav1d flac fontconfig harfbuzz libdrm libpng \ libusb libwebp libxml libxslt openh264 opus || ${FALSE} # Chromium uses an unreleased version of FFmpeg, so configure it cd ${WRKSRC}/media/ffmpeg && \ ${PYTHON_CMD} scripts/build_ffmpeg.py freebsd ${FFMPEG_TARGET} \ --config-only --branding=${FFMPEG_BRAND} cd ${FFMPEG_BDIR} && ${GMAKE} ffversion.h ${MKDIR} ${FFMPEG_CDIR} .for _e in config.h config.asm config_components.asm config_components.h libavcodec libavformat libavutil -${CP} -pR ${FFMPEG_BDIR}/${_e} ${FFMPEG_CDIR} .endfor touch ${WRKDIR}/pulseaudio-${PULSEV}/src/pulse/version.h @${REINPLACE_CMD} -e 's|$${LOCALBASE}|${LOCALBASE}|;s|$${MODCLANG_VERSION}|${LLVM_DEFAULT}|' \ ${WRKSRC}/build/linux/strip_binary.gni \ ${WRKSRC}/build/rust/rust_bindgen.gni \ ${WRKSRC}/build/rust/rust_bindgen_generator.gni pre-build: @${CARGO_CARGO_RUN} build \ --manifest-path ${CARGO_CARGOTOML} \ --verbose \ --verbose \ ${CARGO_BUILD_ARGS} (cd ${WRKSRC}/third_party/devtools-frontend/src/node_modules/@rollup && \ mkdir -p rollup-freebsd && \ cp ${CARGO_TARGET_DIR}/release/libbindings_napi.so \ rollup-freebsd/rollup.freebsd.node && \ echo '{ "main": "./rollup.freebsd.node" }' \ > rollup-freebsd/package.json) do-configure: (cd ${WRKSRC}/esbuild && \ ${LOCALBASE}/bin/node scripts/esbuild.js \ /usr/local/bin/esbuild --neutral && \ rm -rf ${WRKSRC}/third_party/devtools-frontend/src/node_modules/esbuild && \ ln -sf ${WRKSRC}/esbuild/npm/esbuild \ ${WRKSRC}/third_party/devtools-frontend/src/node_modules/esbuild \ ) # GN generator bootstrapping and generating ninja files cd ${WRKSRC} && ${SETENV} ${CONFIGURE_ENV} CC=${CC} CXX=${CXX} LD=${CXX} \ READELF=${READELF} AR=${AR} NM=${NM} ${PYTHON_CMD} \ ./tools/gn/bootstrap/bootstrap.py ${GN_BOOTSTRAP_FLAGS} cd ${WRKSRC} && ${SETENV} ${CONFIGURE_ENV} ./out/${BUILDTYPE}/gn \ gen --args='${GN_ARGS}' out/${BUILDTYPE} # Setup nodejs dependency @${MKDIR} ${WRKSRC}/third_party/node/freebsd/node-freebsd/bin ${LN} -sf ${LOCALBASE}/bin/node ${WRKSRC}/third_party/node/freebsd/node-freebsd/bin/node # Setup rustc dependency @${MKDIR} ${WRKSRC}/third_party/rust-toolchain/bin ${LN} -sf ${LOCALBASE}/bin/rustc ${WRKSRC}/third_party/rust-toolchain/bin/rustc # Setup esbuild dependency ${LN} -sf ${LOCALBASE}/bin/esbuild ${WRKSRC}/third_party/devtools-frontend/src/third_party/esbuild/esbuild # Setup gperf dependency ${LN} -sf ${LOCALBASE}/bin/gperf ${WRKSRC}/third_party/gperf/cipd/bin/gperf # Setup buildtools/freebsd @${MKDIR} ${WRKSRC}/buildtools/freebsd ${LN} -sf ${WRKSRC}/buildtools/linux64/clang-format ${WRKSRC}/buildtools/freebsd ${LN} -sf ${WRKSRC}/out/${BUILDTYPE}/gn ${WRKSRC}/buildtools/freebsd ${LN} -sf /usr/bin/strip ${WRKSRC}/buildtools/freebsd/strip do-install: @${MKDIR} ${STAGEDIR}${DATADIR} ${INSTALL_MAN} ${WRKSRC}/chrome/app/resources/manpage.1.in \ ${STAGEDIR}${PREFIX}/share/man/man1/chrome.1 @${SED} -i "" -e 's,\@\@PACKAGE\@\@,chromium,g;s,\@\@MENUNAME\@\@,Chromium Web Browser,g' \ ${STAGEDIR}${PREFIX}/share/man/man1/chrome.1 ${CP} ${WRKSRC}/chrome/app/theme/chromium/product_logo_22_mono.png ${WRKSRC}/chrome/app/theme/chromium/product_logo_22.png .for s in 22 24 48 64 128 256 @${MKDIR} ${STAGEDIR}${PREFIX}/share/icons/hicolor/${s}x${s}/apps ${INSTALL_DATA} ${WRKSRC}/chrome/app/theme/chromium/product_logo_${s}.png \ ${STAGEDIR}${PREFIX}/share/icons/hicolor/${s}x${s}/apps/chrome.png .endfor ${INSTALL_DATA} ${WRKSRC}/out/${BUILDTYPE}/*.png ${STAGEDIR}${DATADIR} ${INSTALL_DATA} ${WRKSRC}/out/${BUILDTYPE}/*.pak ${STAGEDIR}${DATADIR} .for d in protoc mksnapshot ${INSTALL_PROGRAM} ${WRKSRC}/out/${BUILDTYPE}/${d} ${STAGEDIR}${DATADIR} .endfor .for d in snapshot_blob.bin v8_context_snapshot.bin ${INSTALL_DATA} ${WRKSRC}/out/${BUILDTYPE}/${d} ${STAGEDIR}${DATADIR} .endfor ${INSTALL_PROGRAM} ${WRKSRC}/out/${BUILDTYPE}/chrome \ ${STAGEDIR}${DATADIR} cd ${WRKSRC}/out/${BUILDTYPE} && \ ${COPYTREE_SHARE} "locales resources" ${STAGEDIR}${DATADIR} @${MKDIR} ${STAGEDIR}${DESKTOPDIR} ${INSTALL_DATA} ${WRKDIR}/chromium-browser.desktop \ ${STAGEDIR}${DESKTOPDIR} ${INSTALL_SCRIPT} ${WRKDIR}/chrome ${STAGEDIR}${PREFIX}/bin ${INSTALL_SCRIPT} ${WRKSRC}/chrome/tools/build/linux/chrome-wrapper \ ${STAGEDIR}${DATADIR} # ANGLE, EGL, Vk .for f in libEGL.so libGLESv2.so libVkICD_mock_icd.so ${INSTALL_LIB} ${WRKSRC}/out/${BUILDTYPE}/${f} ${STAGEDIR}${DATADIR} .endfor ${INSTALL_LIB} ${WRKSRC}/out/${BUILDTYPE}/libvulkan.so.1 \ ${STAGEDIR}${DATADIR}/libvulkan.so .if ${BUILDTYPE} == Debug ${INSTALL_LIB} ${WRKSRC}/out/${BUILDTYPE}/libVkLayer_khronos_validation.so ${STAGEDIR}${DATADIR} .endif # SwiftShader .if ${ARCH} != aarch64 ${INSTALL_LIB} ${WRKSRC}/out/${BUILDTYPE}/libvk_swiftshader.so ${STAGEDIR}${DATADIR} ${INSTALL_DATA} ${WRKSRC}/out/${BUILDTYPE}/vk_swiftshader_icd.json ${STAGEDIR}${DATADIR} .endif post-install-DEBUG-on: ${INSTALL_LIB} ${WRKSRC}/out/${BUILDTYPE}/*.so \ ${STAGEDIR}${DATADIR} ${INSTALL_PROGRAM} ${WRKSRC}/out/${BUILDTYPE}/character_data_generator \ ${STAGEDIR}${DATADIR} post-install-DRIVER-on: ${INSTALL_PROGRAM} ${WRKSRC}/out/${BUILDTYPE}/chromedriver.unstripped \ ${STAGEDIR}${PREFIX}/bin/chromedriver do-test-TEST-on: .for t in ${TEST_TARGETS} cd ${WRKSRC}/out/${BUILDTYPE} && \ ./${t} --gtest_filter=-${EXCLUDE_${t}:ts:} || ${TRUE} .endfor .include diff --git a/www/chromium/files/patch-services_device_hid_hid__service__freebsd.cc b/www/chromium/files/patch-services_device_hid_hid__service__freebsd.cc index 59661d7c81ea..6f1a95311fa9 100644 --- a/www/chromium/files/patch-services_device_hid_hid__service__freebsd.cc +++ b/www/chromium/files/patch-services_device_hid_hid__service__freebsd.cc @@ -1,398 +1,397 @@ ---- services/device/hid/hid_service_freebsd.cc.orig 2025-01-19 19:00:05 UTC +--- services/device/hid/hid_service_freebsd.cc.orig 2026-07-02 07:32:08 UTC +++ services/device/hid/hid_service_freebsd.cc -@@ -0,0 +1,395 @@ +@@ -0,0 +1,394 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "services/device/hid/hid_service_freebsd.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include "base/files/file_descriptor_watcher_posix.h" +#include "base/files/file_enumerator.h" +#include "base/files/file_util.h" +#include "base/files/file.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/posix/eintr_wrapper.h" +#include "base/stl_util.h" +#include "base/strings/pattern.h" +#include "base/strings/stringprintf.h" +#include "base/strings/sys_string_conversions.h" +#include "base/strings/string_util.h" +#include "base/strings/string_split.h" +#include "base/task/single_thread_task_runner.h" +#include "base/task/thread_pool.h" +#include "base/threading/scoped_blocking_call.h" +#include "base/threading/thread_restrictions.h" +#include "components/device_event_log/device_event_log.h" +#include "services/device/hid/hid_connection_freebsd.h" + +const int kMaxPermissionChecks = 5; + +namespace device { + +struct HidServiceFreeBSD::ConnectParams { + ConnectParams(scoped_refptr device_info, + bool allow_protected_reports, + bool allow_fido_reports, + ConnectCallback callback) + : device_info(std::move(device_info)), + allow_protected_reports(allow_protected_reports), + allow_fido_reports(allow_fido_reports), + callback(std::move(callback)), + task_runner(base::SequencedTaskRunner::GetCurrentDefault()), + blocking_task_runner( + base::ThreadPool::CreateSequencedTaskRunner(kBlockingTaskTraits)) {} + ~ConnectParams() {} + + scoped_refptr device_info; + bool allow_protected_reports; + bool allow_fido_reports; + ConnectCallback callback; + scoped_refptr task_runner; + scoped_refptr blocking_task_runner; + base::ScopedFD fd; +}; + +class HidServiceFreeBSD::BlockingTaskRunnerHelper { + public: + BlockingTaskRunnerHelper(base::WeakPtr service) + : service_(std::move(service)), + task_runner_(base::SequencedTaskRunner::GetCurrentDefault()) { + DETACH_FROM_SEQUENCE(sequence_checker_); + + timer_.reset(new base::RepeatingTimer()); + devd_buffer_ = new net::IOBufferWithSize(1024); + } + + BlockingTaskRunnerHelper(const BlockingTaskRunnerHelper&) = delete; + BlockingTaskRunnerHelper& operator=(const BlockingTaskRunnerHelper&) = delete; + + ~BlockingTaskRunnerHelper() { + } + + void Start() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + const base::FilePath kDevRoot("/dev"); + const std::string kUHIDPattern("/dev/uhid*"); + + base::FileEnumerator enumerator(kDevRoot, false, base::FileEnumerator::FILES); + do { + const base::FilePath next_device_path(enumerator.Next()); + const std::string next_device = next_device_path.value(); + if (next_device.empty()) + break; + + if (base::MatchPattern(next_device, kUHIDPattern)) + OnDeviceAdded(next_device.substr(5)); + } while (true); + + SetupDevdMonitor(); + + task_runner_->PostTask( + FROM_HERE, + base::BindOnce(&HidServiceFreeBSD::FirstEnumerationComplete, service_)); + } + + bool HaveReadWritePermissions(std::string device_id) { + std::string device_node = "/dev/" + device_id; + base::AssertBlockingAllowed(); + + base::FilePath device_path(device_node); + base::File device_file; + int flags = + base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE; + device_file.Initialize(device_path, flags); + if (!device_file.IsValid()) + return false; + + return true; + } + + void OnDeviceAdded(std::string device_id) { + base::ScopedBlockingCall scoped_blocking_call( + FROM_HERE, base::BlockingType::MAY_BLOCK); + std::string device_node = "/dev/" + device_id; + uint16_t vendor_id = 0xffff; + uint16_t product_id = 0xffff; + std::string product_name = ""; + std::string serial_number = ""; + + std::vector report_descriptor; + + base::AssertBlockingAllowed(); + + base::FilePath device_path(device_node); + base::File device_file; + int flags = + base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE; + device_file.Initialize(device_path, flags); + if (!device_file.IsValid()) { + HID_LOG(ERROR) << "Failed to open '" << device_node + << "': " + << base::File::ErrorToString(device_file.error_details()); + return; + } + + base::ScopedFD fd; + fd.reset(device_file.TakePlatformFile()); + + struct usb_gen_descriptor ugd; + ugd.ugd_data = NULL; + ugd.ugd_maxlen = 0xffff; + int result = HANDLE_EINTR( + ioctl(fd.get(), USB_GET_REPORT_DESC, &ugd)); + + if (result < 0) { + HID_LOG(ERROR) << "Failed to get report descriptor size"; + return; + } + + report_descriptor.resize(ugd.ugd_actlen); + + ugd.ugd_data = report_descriptor.data(); + ugd.ugd_maxlen = ugd.ugd_actlen; + result = HANDLE_EINTR( + ioctl(fd.get(), USB_GET_REPORT_DESC, &ugd)); + + if (result < 0) { + HID_LOG(ERROR) << "Failed to get report descriptor"; + return; + } + + scoped_refptr device_info(new HidDeviceInfo( + device_id, + /*physical_device_id*/"", + vendor_id, + product_id, + product_name, + serial_number, + device::mojom::HidBusType::kHIDBusTypeUSB, + report_descriptor, + device_node)); + + task_runner_->PostTask(FROM_HERE, base::BindOnce(&HidServiceFreeBSD::AddDevice, + service_, device_info)); + } + + void OnDeviceRemoved(std::string device_id) { + base::ScopedBlockingCall scoped_blocking_call( + FROM_HERE, base::BlockingType::MAY_BLOCK); + task_runner_->PostTask( + FROM_HERE, base::BindOnce(&HidServiceFreeBSD::RemoveDevice, service_, + device_id)); + } + + private: + + void CheckPendingPermissionChange() { + base::AssertBlockingAllowed(); + std::map::iterator it; + for (it = permissions_checks_attempts_.begin(); it != permissions_checks_attempts_.end();) { + std::string device_name = it->first; + bool keep = true; + if (HaveReadWritePermissions(device_name)) { + OnDeviceAdded(device_name); + keep = false; + } + else if (it->second-- <= 0) { + HID_LOG(ERROR) << "Still don't have write permissions to '" << device_name + << "' after " << kMaxPermissionChecks << " attempts"; + keep = false; + } + + if (keep) + ++it; + else + permissions_checks_attempts_.erase(it++); + } + + if (permissions_checks_attempts_.empty()) + timer_->Stop(); + } + + void SetupDevdMonitor() { + base::AssertBlockingAllowed(); + + int devd_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0); + if (devd_fd < 0) + return; + + struct sockaddr_un sa; + + sa.sun_family = AF_UNIX; + strlcpy(sa.sun_path, "/var/run/devd.seqpacket.pipe", sizeof(sa.sun_path)); + if (connect(devd_fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) { + close(devd_fd); + return; + } + + devd_fd_.reset(devd_fd); + file_watcher_ = base::FileDescriptorWatcher::WatchReadable( + devd_fd_.get(), base::BindRepeating(&BlockingTaskRunnerHelper::OnDevdMessageCanBeRead, + base::Unretained(this))); + } + + void OnDevdMessageCanBeRead() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + ssize_t bytes_read = HANDLE_EINTR(recv(devd_fd_.get(), devd_buffer_->data(), + devd_buffer_->size() - 1, MSG_WAITALL)); + if (bytes_read < 0) { + if (errno != EAGAIN) { + HID_LOG(ERROR) << "Read failed"; + file_watcher_.reset(); + } + return; + } + + devd_buffer_->data()[bytes_read] = 0; + char *data = devd_buffer_->data(); + // It may take some time for devd to change permissions + // on /dev/uhidX node. So do not fail immediately if + // open fail. Retry each second for kMaxPermissionChecks + // times before giving up entirely + if (base::StartsWith(data, "+uhid", base::CompareCase::SENSITIVE)) { + std::vector parts = base::SplitString( + data, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); + if (!parts.empty()) { + std::string device_name = parts[0].substr(1); // skip '+' + if (HaveReadWritePermissions(device_name)) + OnDeviceAdded(parts[0].substr(1)); + else { + // Do not re-add to checks + if (permissions_checks_attempts_.find(device_name) == permissions_checks_attempts_.end()) { + permissions_checks_attempts_.insert(std::pair(device_name, kMaxPermissionChecks)); + timer_->Start(FROM_HERE, base::Seconds(1), + this, &BlockingTaskRunnerHelper::CheckPendingPermissionChange); + } + } + } + } + + if (base::StartsWith(data, "-uhid", base::CompareCase::SENSITIVE)) { + std::vector parts = base::SplitString( + data, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); + if (!parts.empty()) { + std::string device_name = parts[0].substr(1); // skip '-' + auto it = permissions_checks_attempts_.find(device_name); + if (it != permissions_checks_attempts_.end()) { + permissions_checks_attempts_.erase(it); + if (permissions_checks_attempts_.empty()) + timer_->Stop(); + } + OnDeviceRemoved(parts[0].substr(1)); + } + } + } + + SEQUENCE_CHECKER(sequence_checker_); + + // This weak pointer is only valid when checked on this task runner. + base::WeakPtr service_; + scoped_refptr task_runner_; + std::unique_ptr file_watcher_; + std::unique_ptr timer_; + base::ScopedFD devd_fd_; + scoped_refptr devd_buffer_; + std::map permissions_checks_attempts_; +}; + +HidServiceFreeBSD::HidServiceFreeBSD() + : blocking_task_runner_( + base::ThreadPool::CreateSequencedTaskRunner(kBlockingTaskTraits)), + helper_(nullptr, base::OnTaskRunnerDeleter(blocking_task_runner_)) { + helper_.reset(new BlockingTaskRunnerHelper(weak_factory_.GetWeakPtr())); + blocking_task_runner_->PostTask( + FROM_HERE, + base::BindOnce(&BlockingTaskRunnerHelper::Start, base::Unretained(helper_.get()))); +} + +HidServiceFreeBSD::~HidServiceFreeBSD() { + blocking_task_runner_->DeleteSoon(FROM_HERE, helper_.release()); +} + +base::WeakPtr HidServiceFreeBSD::GetWeakPtr() { + return weak_factory_.GetWeakPtr(); +} + +// static +void HidServiceFreeBSD::OpenOnBlockingThread( + std::unique_ptr params) { + base::ScopedBlockingCall scoped_blocking_call( + FROM_HERE, base::BlockingType::MAY_BLOCK); + scoped_refptr task_runner = params->task_runner; + + base::FilePath device_path(params->device_info->device_node()); + base::File device_file; + int flags = + base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE; + device_file.Initialize(device_path, flags); + if (!device_file.IsValid()) { + HID_LOG(EVENT) << "Failed to open '" << params->device_info->device_node() + << "': " + << base::File::ErrorToString(device_file.error_details()); + task_runner->PostTask(FROM_HERE, + base::BindOnce(std::move(params->callback), nullptr)); + return; + } + params->fd.reset(device_file.TakePlatformFile()); + task_runner->PostTask(FROM_HERE, base::BindOnce(&HidServiceFreeBSD::FinishOpen, + std::move(params))); +} + +void HidServiceFreeBSD::Connect(const std::string& device_guid, + bool allow_protected_reports, + bool allow_fido_reports, + ConnectCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + const auto& map_entry = devices().find(device_guid); + if (map_entry == devices().end()) { + base::SequencedTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, base::BindOnce(std::move(callback), nullptr)); + return; + } + + scoped_refptr device_info = map_entry->second; + + auto params = std::make_unique(device_info, + allow_protected_reports, + allow_fido_reports, + std::move(callback)); + scoped_refptr blocking_task_runner = + params->blocking_task_runner; + + blocking_task_runner->PostTask( + FROM_HERE, base::BindOnce(&HidServiceFreeBSD::OpenOnBlockingThread, + std::move(params))); +} + +// static +void HidServiceFreeBSD::FinishOpen(std::unique_ptr params) { + DCHECK(params->fd.is_valid()); + + if (!base::SetNonBlocking(params->fd.get())) { + HID_PLOG(ERROR) << "Failed to set the non-blocking flag on the device fd"; -+ std::move(params->callback).Run(nullptr); + } + + std::move(params->callback).Run(base::MakeRefCounted( + std::move(params->device_info), + std::move(params->fd), + std::move(params->blocking_task_runner), + params->allow_protected_reports, + params->allow_fido_reports + )); +} + +} // namespace device