mirror of
https://github.com/vanilla-wiiu/vanilla.git
synced 2026-10-28 09:43:51 +00:00
Merge branch 'master' into brightness
This commit is contained in:
commit
1e6370391e
67 files changed
+2365
-3467
No files matched your search
+6
-1
@@ -19,9 +19,14 @@ else()
|
||||
set(vendored_default FALSE)
|
||||
endif()
|
||||
|
||||
set(vanilla_pipe_default OFF)
|
||||
if(LINUX OR ANDROID)
|
||||
set(vanilla_pipe_default ON)
|
||||
endif()
|
||||
|
||||
OPTION(VANILLA_BUILD_GUI "Build SDL2 GUI app" ON)
|
||||
OPTION(VANILLA_BUILD_TESTS "Build unit tests for Vanilla" OFF)
|
||||
OPTION(VANILLA_BUILD_PIPE "Build vanilla-pipe for connecting to Wii U (Linux only)" ${LINUX})
|
||||
OPTION(VANILLA_BUILD_PIPE "Build vanilla-pipe for connecting to Wii U" ${vanilla_pipe_default})
|
||||
OPTION(VANILLA_BUILD_VENDORED "Build Vanilla with \"vendored\" third-party libraries" ${vendored_default})
|
||||
|
||||
add_subdirectory(lib)
|
||||
|
||||
@@ -27,7 +27,7 @@ An Arch User Repository (AUR) package called `vanilla-wiiu-git` is available for
|
||||
- [Nintendo Switch](https://github.com/vanilla-wiiu/vanilla/wiki/Nintendo-Switch)
|
||||
- [Steam Deck](https://github.com/vanilla-wiiu/vanilla/wiki/Steam-Deck)
|
||||
- [Linux](https://github.com/vanilla-wiiu/vanilla/wiki/Linux-Setup-Guide) (check [Wi-Fi hardware compatibility](https://github.com/vanilla-wiiu/vanilla/wiki/Wireless-Compatibility))
|
||||
- [Android](https://github.com/vanilla-wiiu/vanilla/wiki/Android-Setup-Guide) (currently frontend only)
|
||||
- [Android](https://github.com/vanilla-wiiu/vanilla/wiki/Android)
|
||||
- [Windows](https://github.com/vanilla-wiiu/vanilla/wiki/Windows-Setup-Guide) (currently frontend only)
|
||||
- [Raspberry Pi](https://github.com/vanilla-wiiu/vanilla/wiki/Single%E2%80%90Board-Computers) (requires [external Wi-Fi adapter](https://github.com/vanilla-wiiu/vanilla/wiki/Linux-Setup-Guide#raspberry-pi))
|
||||
- [Orange Pi](https://github.com/vanilla-wiiu/vanilla/wiki/Single%E2%80%90Board-Computers) (requires [external Wi-Fi adapter](https://github.com/vanilla-wiiu/vanilla/wiki/Linux-Setup-Guide#raspberry-pi))
|
||||
@@ -90,19 +90,19 @@ Vanilla currently requires the following dependencies:
|
||||
|
||||
- **Debian/Ubuntu**
|
||||
```sh
|
||||
apt install build-essential cmake libsdl2-dev libsdl2-image-dev libsdl2-ttf-dev libavformat-dev libavcodec-dev libavutil-dev libswscale-dev libnl-genl-3-dev libnl-route-3-dev libssl-dev libxml2-dev libnm-dev libpolkit-agent-1-dev
|
||||
apt install build-essential cmake libsdl2-dev libsdl2-image-dev libsdl2-ttf-dev libavformat-dev libavcodec-dev libavutil-dev libswscale-dev libnl-genl-3-dev libnl-route-3-dev libxml2-dev libnm-dev libpolkit-agent-1-dev
|
||||
```
|
||||
- **Fedora**
|
||||
```sh
|
||||
dnf install libavcodec-free-devel libavutil-free-devel libavfilter-free-devel libnl3-devel SDL2-devel SDL2_image-devel SDL2_ttf-devel openssl-devel make automake gcc gcc-c++ kernel-devel cmake libxml2-devel NetworkManager-libnm-devel polkit-devel
|
||||
dnf install libavcodec-free-devel libavutil-free-devel libavfilter-free-devel libnl3-devel SDL2-devel SDL2_image-devel SDL2_ttf-devel make automake gcc gcc-c++ kernel-devel cmake libxml2-devel NetworkManager-libnm-devel polkit-devel
|
||||
```
|
||||
- **Arch**
|
||||
```sh
|
||||
pacman -S base-devel make cmake ffmpeg libnl sdl2 sdl2_image sdl2_ttf libxml2 libnm openssl polkit
|
||||
pacman -S base-devel make cmake ffmpeg libnl sdl2 sdl2_image sdl2_ttf libxml2 libnm polkit
|
||||
```
|
||||
- **Alpine/postmarketOS**
|
||||
```sh
|
||||
apk add build-base cmake sdl2-dev sdl2_image-dev sdl2_ttf-dev ffmpeg-dev libnl3-dev libxml2-dev openssl-dev networkmanager-dev polkit-dev
|
||||
apk add build-base cmake sdl2-dev sdl2_image-dev sdl2_ttf-dev ffmpeg-dev libnl3-dev libxml2-dev networkmanager-dev polkit-dev
|
||||
```
|
||||
|
||||
The build process is otherwise normal for a CMake program:
|
||||
|
||||
@@ -13,7 +13,7 @@ android {
|
||||
}
|
||||
compileSdkVersion 34
|
||||
defaultConfig {
|
||||
minSdkVersion 19
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 34
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
@@ -23,7 +23,7 @@ android {
|
||||
// abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
|
||||
// }
|
||||
cmake {
|
||||
arguments "-DANDROID_APP_PLATFORM=android-19", "-DANDROID_STL=c++_static"
|
||||
arguments "-DANDROID_APP_PLATFORM=android-21", "-DANDROID_STL=c++_static"
|
||||
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
|
||||
version "3.31"
|
||||
}
|
||||
@@ -35,6 +35,13 @@ android {
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
packagingOptions {
|
||||
jniLibs {
|
||||
// Ensure libvanilla_pipe.so is accessible alongside main executable
|
||||
useLegacyPackaging true
|
||||
}
|
||||
}
|
||||
sourceSets.main.assets.setSrcDirs(['../../gui/assets'])
|
||||
applicationVariants.all { variant ->
|
||||
tasks["merge${variant.name.capitalize()}Assets"]
|
||||
.dependsOn("externalNativeBuild${variant.name.capitalize()}")
|
||||
|
||||
@@ -7,4 +7,4 @@
|
||||
APP_ABI := armeabi-v7a arm64-v8a x86 x86_64
|
||||
|
||||
# Min runtime API level
|
||||
APP_PLATFORM=android-16
|
||||
APP_PLATFORM=android-21
|
||||
@@ -9,5 +9,5 @@ project(GAME)
|
||||
# Android has no need for a "windowed" option
|
||||
set(VANILLA_GUI_ENABLE_WINDOWED OFF)
|
||||
|
||||
# Your game and its CMakeLists.txt are in a subfolder named "src"
|
||||
add_subdirectory(../../.. "${CMAKE_SOURCE_DIR}/build")
|
||||
# Add source files and keep in individual per-ABI dirs
|
||||
add_subdirectory(../../.. "${CMAKE_CURRENT_BINARY_DIR}/vanilla")
|
||||
Vendored
+6
@@ -96,3 +96,9 @@
|
||||
void hapticRun(int, float, int);
|
||||
void hapticStop(int);
|
||||
}
|
||||
|
||||
-keepclassmembers class com.mattkc.vanilla.VanillaActivity {
|
||||
public static android.view.Surface createVideoSurface(int, int, int);
|
||||
public static float[] updateVideoSurfaceTexture();
|
||||
public static void destroyVideoSurface();
|
||||
}
|
||||
@@ -1,18 +1,59 @@
|
||||
package com.mattkc.vanilla;
|
||||
|
||||
import android.graphics.SurfaceTexture;
|
||||
import android.view.Surface;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.libsdl.app.SDLActivity;
|
||||
|
||||
public class VanillaActivity extends SDLActivity {
|
||||
private static SurfaceTexture videoSurfaceTexture;
|
||||
private static Surface videoSurface;
|
||||
private static final AtomicBoolean videoFrameAvailable = new AtomicBoolean(false);
|
||||
private static final float[] videoTransform = new float[16];
|
||||
|
||||
@Override
|
||||
protected String[] getLibraries() {
|
||||
return new String[] {
|
||||
"SDL2",
|
||||
"SDL2_image",
|
||||
// "SDL2_mixer",
|
||||
// "SDL2_net",
|
||||
"SDL2_ttf",
|
||||
"xml2",
|
||||
"vanilla"
|
||||
};
|
||||
}
|
||||
|
||||
public static synchronized Surface createVideoSurface(int textureName, int width, int height) {
|
||||
destroyVideoSurface();
|
||||
|
||||
videoSurfaceTexture = new SurfaceTexture(textureName);
|
||||
videoSurfaceTexture.setDefaultBufferSize(width, height);
|
||||
videoSurfaceTexture.setOnFrameAvailableListener(surfaceTexture -> videoFrameAvailable.set(true));
|
||||
videoSurface = new Surface(videoSurfaceTexture);
|
||||
return videoSurface;
|
||||
}
|
||||
|
||||
// Called from SDL render thread where owning GLES context is current
|
||||
public static synchronized float[] updateVideoSurfaceTexture() {
|
||||
if (videoSurfaceTexture == null || !videoFrameAvailable.getAndSet(false)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
videoSurfaceTexture.updateTexImage();
|
||||
videoSurfaceTexture.getTransformMatrix(videoTransform);
|
||||
return videoTransform;
|
||||
}
|
||||
|
||||
public static synchronized void destroyVideoSurface() {
|
||||
videoFrameAvailable.set(false);
|
||||
if (videoSurface != null) {
|
||||
videoSurface.release();
|
||||
videoSurface = null;
|
||||
}
|
||||
if (videoSurfaceTexture != null) {
|
||||
videoSurfaceTexture.release();
|
||||
videoSurfaceTexture = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM docker.io/ubuntu:24.04
|
||||
FROM docker.io/ubuntu:24.04@sha256:33ceb71981b602c1a7443a53469e4dba065f7503eab3078a2d7a57a2ab987517
|
||||
|
||||
# Install build dependencies
|
||||
RUN apt update && \
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM docker.io/debian:bookworm
|
||||
FROM docker.io/debian:bookworm@sha256:6ebd97fa83deb272194a2cf015b3d26a4d538e9ad3a7a79d544c8af5b0a01443
|
||||
|
||||
ARG ARCH=x86_64
|
||||
ENV ARCH=${ARCH}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM docker.io/ubuntu:24.04
|
||||
FROM docker.io/ubuntu:24.04@sha256:33ceb71981b602c1a7443a53469e4dba065f7503eab3078a2d7a57a2ab987517
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
ENV TZ=Etc/UTC
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM docker.io/debian:trixie
|
||||
FROM docker.io/debian:trixie@sha256:f324c7ff54321e8d9c588493a20244965938ce0aa50bbd1022d38010e9ffc4b1
|
||||
|
||||
ARG ARCH=x86_64
|
||||
ENV ARCH=${ARCH}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM docker.io/debian:trixie
|
||||
FROM docker.io/debian:trixie@sha256:f324c7ff54321e8d9c588493a20244965938ce0aa50bbd1022d38010e9ffc4b1
|
||||
|
||||
ARG ARCH=x86_64
|
||||
ENV ARCH=${ARCH}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM docker.io/debian:trixie
|
||||
FROM docker.io/debian:trixie@sha256:f324c7ff54321e8d9c588493a20244965938ce0aa50bbd1022d38010e9ffc4b1
|
||||
|
||||
ARG ARCH=x86_64
|
||||
ENV ARCH=${ARCH}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM docker.io/debian:trixie
|
||||
FROM docker.io/debian:trixie@sha256:f324c7ff54321e8d9c588493a20244965938ce0aa50bbd1022d38010e9ffc4b1
|
||||
|
||||
ARG ARCH=x86_64
|
||||
ENV ARCH=${ARCH}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM docker.io/debian:trixie
|
||||
FROM docker.io/debian:trixie@sha256:f324c7ff54321e8d9c588493a20244965938ce0aa50bbd1022d38010e9ffc4b1
|
||||
|
||||
ARG ARCH=x86_64
|
||||
ENV ARCH=${ARCH}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM docker.io/ubuntu:24.04
|
||||
FROM docker.io/ubuntu:24.04@sha256:33ceb71981b602c1a7443a53469e4dba065f7503eab3078a2d7a57a2ab987517
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
ENV TZ=Etc/UTC
|
||||
|
||||
+17
-8
@@ -87,6 +87,11 @@ function(compile_ffmpeg _target_name _system_name _system_processor)
|
||||
)
|
||||
elseif(ANDROID)
|
||||
set(FFMPEG_TARGET_OS "android")
|
||||
list(APPEND FFMPEG_CONFIGURE_ARGS
|
||||
--enable-jni
|
||||
--enable-mediacodec
|
||||
--enable-decoder=h264_mediacodec
|
||||
)
|
||||
|
||||
# Get Android platform version (inexplicably apparently there literally isn't a better way to do this)
|
||||
string(REPLACE "-" " " _android_platform_args ${ANDROID_PLATFORM})
|
||||
@@ -296,6 +301,13 @@ list(APPEND VANILLA_GUI_SRC
|
||||
ui/ui_keyboard.c
|
||||
)
|
||||
|
||||
if (ANDROID)
|
||||
list(APPEND VANILLA_GUI_SRC
|
||||
platform_android.c
|
||||
ui/ui_sdl_android.c
|
||||
)
|
||||
endif()
|
||||
|
||||
if (LINUX)
|
||||
list(APPEND VANILLA_GUI_SRC
|
||||
ui/ui_sdl_linux.c
|
||||
@@ -390,7 +402,10 @@ target_link_libraries(vanillagui PRIVATE
|
||||
if (ANDROID)
|
||||
target_link_libraries(vanillagui PRIVATE
|
||||
SDL2main
|
||||
android
|
||||
GLESv2
|
||||
log
|
||||
mediandk
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -491,19 +506,13 @@ target_include_directories(vanillagui PRIVATE
|
||||
)
|
||||
|
||||
# Install assets
|
||||
if(ANDROID)
|
||||
# Put assets in correct place relative to build
|
||||
add_custom_command(
|
||||
TARGET vanilla POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/assets" "${CMAKE_CURRENT_SOURCE_DIR}/../android/app/src/main/assets"
|
||||
)
|
||||
elseif(APPLE)
|
||||
if(APPLE)
|
||||
# Put assets in correct place relative to build
|
||||
add_custom_command(
|
||||
TARGET vanilla POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory_if_different "${CMAKE_CURRENT_SOURCE_DIR}/assets" "$<TARGET_FILE_DIR:vanilla>/../Resources/assets"
|
||||
)
|
||||
else()
|
||||
elseif(NOT ANDROID)
|
||||
# Put assets in correct place relative to build
|
||||
add_custom_command(
|
||||
TARGET vanilla POST_BUILD
|
||||
|
||||
+2
-1
@@ -58,7 +58,8 @@ static const char *lang_str[__VPI_LANG_T_COUNT] = {
|
||||
"Reset",
|
||||
"Full Screen",
|
||||
"Hardware Decoding",
|
||||
"Cursor In Full Screen"
|
||||
"Cursor In Full Screen",
|
||||
"Local connection on Android requires root access."
|
||||
};
|
||||
|
||||
const char *lang(vpi_lang_t id)
|
||||
|
||||
@@ -60,6 +60,7 @@ typedef enum {
|
||||
VPI_LANG_FULLSCREEN,
|
||||
VPI_LANG_HWDEC,
|
||||
VPI_LANG_CURSOR_IN_FS,
|
||||
VPI_LANG_ANDROID_ROOT_REQUIRED,
|
||||
__VPI_LANG_T_COUNT
|
||||
} vpi_lang_t;
|
||||
|
||||
|
||||
@@ -5,10 +5,14 @@
|
||||
typedef uint32_t in_addr_t;
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#ifdef ANDROID
|
||||
#include <net/if.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -17,6 +21,10 @@ typedef uint32_t in_addr_t;
|
||||
#include "menu_common.h"
|
||||
#include "menu_settings.h"
|
||||
#include "pipemgmt.h"
|
||||
#include "platform.h"
|
||||
#ifdef ANDROID
|
||||
#include "platform_android.h"
|
||||
#endif
|
||||
#include "ui/ui_anim.h"
|
||||
#include "ui/ui_util.h"
|
||||
|
||||
@@ -26,8 +34,10 @@ static int error_lbl;
|
||||
static int ip_textedit;
|
||||
|
||||
#define MAX_WIRELESS_ENTRIES 4
|
||||
static size_t detected_wireless_interface_count = 0;
|
||||
static char detected_wireless_interfaces[256][256];
|
||||
static int intf_start = 0;
|
||||
#define MAX_DETECTED_WIRELESS_INTERFACES (sizeof(detected_wireless_interfaces) / sizeof(detected_wireless_interfaces[0]))
|
||||
static int intf_start = 0; // NOTE: Not currently implemented, but may be used for pagination in the future
|
||||
static int intf_buttons[MAX_WIRELESS_ENTRIES];
|
||||
|
||||
static const char *checkmark = 0;//"checkmark.svg";
|
||||
@@ -66,13 +76,39 @@ static void return_to_connection(vui_context_t *vui, int btn, void *v)
|
||||
vui_transition_fade_layer_out(vui, layer, cb, cb_data);
|
||||
}
|
||||
|
||||
static void populate_wireless_intf()
|
||||
static void add_wireless_interface(const char *intf)
|
||||
{
|
||||
// LINUX ONLY:
|
||||
// Sanity checks
|
||||
if (!intf || !intf[0] || detected_wireless_interface_count >= MAX_DETECTED_WIRELESS_INTERFACES) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure interface hasn't already been added
|
||||
for (size_t i = 0; i < detected_wireless_interface_count; i++) {
|
||||
if (!strcmp(detected_wireless_interfaces[i], intf)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Add interface
|
||||
vui_strncpy(
|
||||
detected_wireless_interfaces[detected_wireless_interface_count],
|
||||
intf,
|
||||
sizeof(detected_wireless_interfaces[detected_wireless_interface_count]));
|
||||
detected_wireless_interface_count++;
|
||||
}
|
||||
|
||||
static void populate_wireless_intf(void)
|
||||
{
|
||||
#ifdef ANDROID
|
||||
static const char *android_prefixes[] = {"wlan", "wifi", "swlan", "mlan", "ath"};
|
||||
static const size_t android_prefix_count = sizeof(android_prefixes) / sizeof(android_prefixes[0]);
|
||||
#endif
|
||||
|
||||
struct dirent *dir;
|
||||
DIR *d = opendir("/sys/class/net");
|
||||
char buf[1024];
|
||||
size_t cur = 0;
|
||||
detected_wireless_interface_count = 0;
|
||||
if (d) {
|
||||
while ((dir = readdir(d)) != 0) {
|
||||
const char *intf = dir->d_name;
|
||||
@@ -80,14 +116,38 @@ static void populate_wireless_intf()
|
||||
snprintf(buf, sizeof(buf), "/sys/class/net/%s/wireless", intf);
|
||||
DIR *wifi_check = opendir(buf);
|
||||
if (wifi_check) {
|
||||
vui_strncpy(detected_wireless_interfaces[cur], intf, sizeof(detected_wireless_interfaces[cur]));
|
||||
cur++;
|
||||
add_wireless_interface(intf);
|
||||
closedir(wifi_check);
|
||||
} else {
|
||||
#ifdef ANDROID
|
||||
// Fallback heuristic approach for Android
|
||||
for (size_t prefix = 0; prefix < android_prefix_count; prefix++) {
|
||||
const char *p = android_prefixes[prefix];
|
||||
if (!strncmp(intf, p, strlen(p))) {
|
||||
add_wireless_interface(intf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(d);
|
||||
}
|
||||
|
||||
#ifdef ANDROID
|
||||
// Some Android releases may restrict /sys/class/net altogether. Probe
|
||||
// heuristically.
|
||||
for (size_t prefix = 0; prefix < android_prefix_count; prefix++) {
|
||||
for (unsigned int index = 0; index < 8; index++) {
|
||||
char intf[IF_NAMESIZE];
|
||||
snprintf(intf, sizeof(intf), "%s%u", android_prefixes[prefix], index);
|
||||
if (if_nametoindex(intf)) {
|
||||
add_wireless_interface(intf);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void intf_pressed(vui_context_t *vui, int button, void *v)
|
||||
@@ -241,6 +301,7 @@ void vpi_menu_connection_and_return_to(vui_context_t *vui, int fade_fglayer, vui
|
||||
|
||||
int is_local = vpi_config.server_address == VANILLA_ADDRESS_LOCAL;
|
||||
int is_via_server = vpi_config.server_address != VANILLA_ADDRESS_LOCAL;
|
||||
int local_available = 1;
|
||||
|
||||
const int lbl2_width = bkg_rect.w*2/3;
|
||||
const char *lbl2_txt;
|
||||
@@ -250,6 +311,15 @@ void vpi_menu_connection_and_return_to(vui_context_t *vui, int fade_fglayer, vui
|
||||
#else
|
||||
lbl2_txt = lang(VPI_LANG_NO_LOCAL_AVAILABLE);
|
||||
is_local = 0;
|
||||
local_available = 0;
|
||||
#endif
|
||||
|
||||
#ifdef ANDROID
|
||||
if (local_available && !vpi_android_has_root_access()) {
|
||||
lbl2_txt = lang(VPI_LANG_ANDROID_ROOT_REQUIRED);
|
||||
is_local = 0;
|
||||
local_available = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
vui_label_create(vui, bkg_rect.x + bkg_rect.w/2 - lbl2_width/2, bkg_rect.y + lbl_margin + bkg_rect.h/8, lbl2_width, bkg_rect.h - lbl_margin - lbl_margin, lbl2_txt, vui_color_create(0.66f,0.66f,0.66f,1), VUI_FONT_SIZE_SMALL, fglayer);
|
||||
@@ -263,9 +333,7 @@ void vpi_menu_connection_and_return_to(vui_context_t *vui, int fade_fglayer, vui
|
||||
vui_button_update_checked(vui, local_btn, is_local);
|
||||
vui_button_update_checked(vui, via_server_btn, is_via_server);
|
||||
|
||||
#ifndef VANILLA_PIPE_AVAILABLE
|
||||
vui_button_update_enabled(vui, local_btn, 0);
|
||||
#endif
|
||||
vui_button_update_enabled(vui, local_btn, local_available);
|
||||
|
||||
// Back button
|
||||
vpi_menu_create_back_button(vui, fglayer, return_to_settings, (void *) (intptr_t) bglayer);
|
||||
|
||||
+65
-18
@@ -5,6 +5,7 @@
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavutil/channel_layout.h>
|
||||
#include <libavutil/hwcontext.h>
|
||||
#include <libavutil/opt.h>
|
||||
#include <libavutil/time.h>
|
||||
#include <libswscale/swscale.h>
|
||||
@@ -22,6 +23,11 @@
|
||||
#include "ui/ui_anim.h"
|
||||
#include "ui/ui_util.h"
|
||||
|
||||
#ifdef ANDROID
|
||||
#include <libavutil/hwcontext_mediacodec.h>
|
||||
#include "ui/ui_sdl_android.h"
|
||||
#endif
|
||||
|
||||
#ifdef VANILLA_V4L2REQUEST_AVAILABLE
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
@@ -242,6 +248,21 @@ static enum AVPixelFormat drm_get_format(struct AVCodecContext *s, const enum AV
|
||||
return AV_PIX_FMT_NONE;
|
||||
}
|
||||
|
||||
#ifdef ANDROID
|
||||
static enum AVPixelFormat mediacodec_get_format(struct AVCodecContext *s,
|
||||
const enum AVPixelFormat *fmt)
|
||||
{
|
||||
while (*fmt != AV_PIX_FMT_NONE) {
|
||||
if (*fmt == AV_PIX_FMT_MEDIACODEC) {
|
||||
return *fmt;
|
||||
}
|
||||
fmt++;
|
||||
}
|
||||
|
||||
return AV_PIX_FMT_NONE;
|
||||
}
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
const AVCodec *codec;
|
||||
@@ -249,6 +270,9 @@ typedef struct {
|
||||
} hwdec_t;
|
||||
|
||||
enum HwDecoderType {
|
||||
#ifdef ANDROID
|
||||
HWDEC_TYPE_MEDIACODEC,
|
||||
#endif
|
||||
HWDEC_TYPE_NVDEC,
|
||||
HWDEC_TYPE_VAAPI,
|
||||
HWDEC_TYPE_V4L2REQUEST,
|
||||
@@ -356,27 +380,24 @@ int open_decoder(vpi_decode_state_t *s, hwdec_t *dec)
|
||||
s->codec_ctx->get_format = dec->get_format;
|
||||
}
|
||||
|
||||
const int BUILD_AVCC = 0;
|
||||
if (BUILD_AVCC) {
|
||||
uint8_t sps[100], pps[100];
|
||||
uint16_t sps_size = vanilla_generate_sps_params(sps, sizeof(sps));
|
||||
uint16_t pps_size = vanilla_generate_pps_params(pps, sizeof(pps));
|
||||
|
||||
const uint8_t *p_sps = sps;
|
||||
const uint8_t *p_pps = pps;
|
||||
|
||||
s->codec_ctx->extradata = av_malloc(1024);
|
||||
s->codec_ctx->extradata_size = build_avcc(
|
||||
s->codec_ctx->extradata, 1024,
|
||||
&p_sps, &sps_size, 1,
|
||||
&p_pps, &pps_size, 1,
|
||||
4
|
||||
);
|
||||
if (!strcmp(dec->codec->name, "h264_mediacodec")) {
|
||||
// MediaCodec requires the AVCC/SPS/PPS upfront
|
||||
uint8_t header[200];
|
||||
size_t header_size = vanilla_generate_h264_header(header, sizeof(header));
|
||||
s->codec_ctx->extradata = av_mallocz(header_size + AV_INPUT_BUFFER_PADDING_SIZE);
|
||||
if (!s->codec_ctx->extradata) {
|
||||
vpilog("Failed to allocate H.264 extradata\n");
|
||||
return VANILLA_ERR_GENERIC;
|
||||
}
|
||||
memcpy(s->codec_ctx->extradata, header, header_size);
|
||||
s->codec_ctx->extradata_size = (int) header_size;
|
||||
s->codec_ctx->width = 854;
|
||||
s->codec_ctx->height = 480;
|
||||
}
|
||||
|
||||
ffmpeg_err = avcodec_open2(s->codec_ctx, dec->codec, NULL);
|
||||
if (ffmpeg_err < 0) {
|
||||
vpilog("Failed to open decoder: %i\n", ffmpeg_err);
|
||||
vpilog("Failed to open decoder %s: %s (%i)\n", dec->name, av_err2str(ffmpeg_err), ffmpeg_err);
|
||||
return VANILLA_ERR_GENERIC;
|
||||
}
|
||||
|
||||
@@ -394,6 +415,12 @@ int vpi_decode_init(vpi_decode_state_t *s)
|
||||
// Initialize decoding context, preferring hardware decoding when available
|
||||
hwdec_t decoders[HWDEC_TYPE_COUNT];
|
||||
|
||||
#ifdef ANDROID
|
||||
decoders[HWDEC_TYPE_MEDIACODEC].name = "MediaCodec";
|
||||
decoders[HWDEC_TYPE_MEDIACODEC].codec = avcodec_find_decoder_by_name("h264_mediacodec");
|
||||
decoders[HWDEC_TYPE_MEDIACODEC].get_format = mediacodec_get_format;
|
||||
#endif
|
||||
|
||||
decoders[HWDEC_TYPE_NVDEC].name = "NVDEC";
|
||||
decoders[HWDEC_TYPE_NVDEC].codec = avcodec_find_decoder_by_name("h264_cuvid");
|
||||
decoders[HWDEC_TYPE_NVDEC].get_format = nvdec_get_format;
|
||||
@@ -418,6 +445,26 @@ int vpi_decode_init(vpi_decode_state_t *s)
|
||||
int r = VANILLA_ERR_GENERIC;
|
||||
|
||||
if (!vpi_config.force_software_decode) {
|
||||
#ifdef ANDROID
|
||||
// MediaCodec renders decoded buffers directly into the SurfaceTexture
|
||||
// owned by SDL's external OES texture.
|
||||
if (r != VANILLA_SUCCESS && vui_sdl_android_get_video_surface()) {
|
||||
vpi_decode_exit(s);
|
||||
s->hw_device_ctx = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_MEDIACODEC);
|
||||
if (s->hw_device_ctx) {
|
||||
AVHWDeviceContext *device_ctx = (AVHWDeviceContext *) s->hw_device_ctx->data;
|
||||
AVMediaCodecDeviceContext *mediacodec_ctx = (AVMediaCodecDeviceContext *) device_ctx->hwctx;
|
||||
mediacodec_ctx->surface = vui_sdl_android_get_video_surface();
|
||||
|
||||
ffmpeg_err = av_hwdevice_ctx_init(s->hw_device_ctx);
|
||||
if (ffmpeg_err >= 0) {
|
||||
r = open_decoder(s, &decoders[HWDEC_TYPE_MEDIACODEC]);
|
||||
} else {
|
||||
vpilog("Failed to initialize MediaCodec device: %s\n", av_err2str(ffmpeg_err));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef VANILLA_CUDA_AVAILABLE
|
||||
// See if we can create an NVDEC context (most NVIDIA GPUs)
|
||||
if (r != VANILLA_SUCCESS) {
|
||||
@@ -1085,8 +1132,8 @@ void vpi_display_update(vui_context_t *vui, int64_t time, void *v)
|
||||
break;
|
||||
default:
|
||||
// Something went wrong, assume we must fail and report to user
|
||||
pthread_join(vpi_event_thread, 0);
|
||||
vanilla_stop();
|
||||
pthread_join(vpi_event_thread, 0);
|
||||
if (vpi_game_queued_error != VANILLA_ERR_SHUTDOWN) {
|
||||
show_error(vui, (void*)(intptr_t) vpi_game_queued_error);
|
||||
} else {
|
||||
|
||||
+77
-21
@@ -1,3 +1,5 @@
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include "pipemgmt.h"
|
||||
|
||||
#ifdef VANILLA_PIPE_AVAILABLE
|
||||
@@ -15,6 +17,11 @@
|
||||
#include <unistd.h>
|
||||
#include <vanilla.h>
|
||||
|
||||
#ifdef ANDROID
|
||||
#include <dlfcn.h>
|
||||
#include <limits.h>
|
||||
#endif
|
||||
|
||||
#include "config.h"
|
||||
#include "platform.h"
|
||||
|
||||
@@ -193,7 +200,7 @@ void *vpi_pipe_log_thread(void *data)
|
||||
if (buf[buf_read] == '\n') {
|
||||
// Print line and reset
|
||||
buf[buf_read + 1] = '\0';
|
||||
vpilog(buf);
|
||||
vpilog("%s", buf);
|
||||
|
||||
buf_read = 0;
|
||||
} else {
|
||||
@@ -270,9 +277,6 @@ void vpi_cancel_pw()
|
||||
potential_pipe_pid = -1;
|
||||
close(in_pipes[1]);
|
||||
close(err_pipes[0]);
|
||||
|
||||
close(err_pipes[1]);
|
||||
close(in_pipes[0]);
|
||||
}
|
||||
vpi_close_polkit_session();
|
||||
}
|
||||
@@ -290,19 +294,28 @@ int vpi_start_epilog()
|
||||
int ret = VANILLA_ERR_GENERIC;
|
||||
|
||||
char ready_buf[500];
|
||||
memset(ready_buf, 0, sizeof(ready_buf));
|
||||
|
||||
while (1) {
|
||||
size_t read_count = 0;
|
||||
int reached_eof = 0;
|
||||
while (read_count + 1 < sizeof(ready_buf)) {
|
||||
char c;
|
||||
while (read_count < sizeof(ready_buf) && read(err_pipes[0], &c, 1) == 1) {
|
||||
ssize_t read_size = read(err_pipes[0], &c, 1);
|
||||
if (read_size < 0 && errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
if (read_size != 1) {
|
||||
reached_eof = 1;
|
||||
break;
|
||||
}
|
||||
if (c == '\n') {
|
||||
break;
|
||||
}
|
||||
ready_buf[read_count] = c;
|
||||
read_count++;
|
||||
ready_buf[read_count++] = c;
|
||||
}
|
||||
if (!memcmp(ready_buf, "READY", 5)) {
|
||||
ready_buf[read_count] = 0;
|
||||
|
||||
if (!strcmp(ready_buf, "READY")) {
|
||||
ret = VANILLA_SUCCESS;
|
||||
|
||||
pipe_input = in_pipes[1];
|
||||
@@ -313,24 +326,27 @@ int vpi_start_epilog()
|
||||
pipe_pid = potential_pipe_pid;
|
||||
|
||||
break;
|
||||
} else {
|
||||
// vpilog("GOT INVALID SIGNAL: %.*s\n", sizeof(ready_buf), ready_buf);
|
||||
vpilog("%.*s\n", sizeof(ready_buf), ready_buf);
|
||||
}
|
||||
|
||||
if (read_count) {
|
||||
vpilog("%s\n", ready_buf);
|
||||
}
|
||||
|
||||
if (reached_eof) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret != VANILLA_SUCCESS) {
|
||||
if (potential_pipe_pid != -1) {
|
||||
kill(potential_pipe_pid, SIGKILL);
|
||||
waitpid(potential_pipe_pid, NULL, 0);
|
||||
potential_pipe_pid = -1;
|
||||
}
|
||||
|
||||
close(in_pipes[1]);
|
||||
close(err_pipes[0]);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
close(err_pipes[1]);
|
||||
close(in_pipes[0]);
|
||||
|
||||
#ifdef VANILLA_POLKIT_AVAILABLE
|
||||
vpi_close_polkit_session();
|
||||
@@ -437,30 +453,70 @@ int vpi_start_pipe()
|
||||
|
||||
setsid();
|
||||
|
||||
// Get current working directory
|
||||
// Execute process (this will replace the running code)
|
||||
#ifdef ANDROID
|
||||
// HACK: Use dladdr to get the path of us
|
||||
Dl_info info;
|
||||
if (!dladdr((void *) &vpi_start_pipe, &info) || !info.dli_fname) {
|
||||
vpilog("Could not get running path of current Android executable\n");
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
// Copy path into our own string
|
||||
char our_path[PATH_MAX];
|
||||
if (snprintf(our_path, sizeof(our_path), "%s", info.dli_fname) >= sizeof(our_path)) {
|
||||
vpilog("Android library path too long - this is a bug, tell maintainers\n");
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
// Get dirname of string and add libvanilla_pipe.so
|
||||
char *library_dir = dirname(our_path);
|
||||
char pipe_path[PATH_MAX];
|
||||
if (snprintf(pipe_path, sizeof(pipe_path), "%s/libvanilla_pipe.so", library_dir) >= sizeof(pipe_path)) {
|
||||
vpilog("Android pipe path too long - this is a bug, tell maintainers\n");
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
char command[PATH_MAX + 256];
|
||||
snprintf(command, sizeof(command), "exec '%s' -local '%s'", pipe_path, vpi_config.wireless_interface);
|
||||
r = execlp("su", "su", "-c", command, (const char *) 0);
|
||||
#else
|
||||
char exe[4096];
|
||||
ssize_t link_len = readlink("/proc/self/exe", exe, sizeof(exe));
|
||||
ssize_t link_len = readlink("/proc/self/exe", exe, sizeof(exe) - 1);
|
||||
if (link_len <= 0) {
|
||||
_exit(1);
|
||||
}
|
||||
exe[link_len] = 0;
|
||||
dirname(exe);
|
||||
strcat(exe, "/vanilla-pipe");
|
||||
|
||||
// Execute process (this will replace the running code)
|
||||
#ifdef VANILLA_POLKIT_AVAILABLE
|
||||
const char *pkexec = "pkexec";
|
||||
r = execlp(pkexec, pkexec, exe, "-local", vpi_config.wireless_interface, (const char *) 0);
|
||||
#else
|
||||
r = execlp(exe, exe, "-local", vpi_config.wireless_interface, (const char *) 0);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
// Handle failure to execute, use _exit so we don't interfere with the host
|
||||
_exit(1);
|
||||
} else if (pid < 0) {
|
||||
// Fork error
|
||||
close(in_pipes[0]);
|
||||
close(in_pipes[1]);
|
||||
close(err_pipes[0]);
|
||||
close(err_pipes[1]);
|
||||
return VANILLA_ERR_GENERIC;
|
||||
} else {
|
||||
// Continuation of parent
|
||||
int ret = VANILLA_ERR_GENERIC;
|
||||
|
||||
// Only the child writes stderr and reads stdin. Closing the parent's
|
||||
// copies here lets startup detect a child that exits before READY.
|
||||
close(err_pipes[1]);
|
||||
close(in_pipes[0]);
|
||||
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = sigint_handler;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
#include "platform_android.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
// Check for whether we have root access on Android (required to run vanilla-pipe)
|
||||
int vpi_android_has_root_access(void)
|
||||
{
|
||||
static int cached_result = -1;
|
||||
if (cached_result >= 0) {
|
||||
return cached_result;
|
||||
}
|
||||
|
||||
// Attempt to run `id -u` as superuser - if it works, it'll return "0" meaning root
|
||||
FILE *command = popen("su -c 'id -u' 2>/dev/null", "r");
|
||||
if (!command) {
|
||||
cached_result = 0;
|
||||
return cached_result;
|
||||
}
|
||||
|
||||
// See if command indeed returned "0"
|
||||
int reported_root = 0;
|
||||
char line[128];
|
||||
while (fgets(line, sizeof(line), command)) {
|
||||
char *end = NULL;
|
||||
errno = 0;
|
||||
long uid = strtol(line, &end, 10);
|
||||
while (end && isspace((unsigned char) *end)) {
|
||||
end++;
|
||||
}
|
||||
if (!errno && end != line && end && !*end && uid == 0) {
|
||||
reported_root = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up, cache and return result
|
||||
int status = pclose(command);
|
||||
cached_result = reported_root && status >= 0 && WIFEXITED(status) && WEXITSTATUS(status) == 0;
|
||||
return cached_result;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#ifndef VANILLA_PI_PLATFORM_ANDROID_H
|
||||
#define VANILLA_PI_PLATFORM_ANDROID_H
|
||||
|
||||
int vpi_android_has_root_access(void);
|
||||
|
||||
#endif // VANILLA_PI_PLATFORM_ANDROID_H
|
||||
+265
-87
@@ -1,7 +1,6 @@
|
||||
#include "ui_sdl.h"
|
||||
|
||||
#include <stdatomic.h>
|
||||
#include <errno.h>
|
||||
#include <math.h>
|
||||
#include <SDL2/SDL.h>
|
||||
#include <SDL2/SDL_hints.h>
|
||||
@@ -15,6 +14,11 @@
|
||||
#include <libavutil/hwcontext.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef ANDROID
|
||||
#include <libavcodec/mediacodec.h>
|
||||
#include "ui_sdl_android.h"
|
||||
#endif
|
||||
|
||||
#ifdef VANILLA_HAS_EGL
|
||||
#include <SDL2/SDL_egl.h>
|
||||
#include <SDL2/SDL_opengl.h>
|
||||
@@ -57,6 +61,11 @@
|
||||
#define AUDIO_BUFFER_SIZE 1664 // Wii U never deviates from this so we can hardcode it
|
||||
#define AUDIO_BUFFER_COUNT 4
|
||||
|
||||
// Our tick rate is 180Hz, which matches the original gamepad input rate.
|
||||
// Our render tick rate is 60Hz (or 1/3 the input tick rate).
|
||||
#define VUI_INPUT_TICK_RATE 180
|
||||
#define VUI_RENDER_TICK_DIVISOR 3
|
||||
|
||||
typedef struct {
|
||||
SDL_Texture *texture;
|
||||
int w;
|
||||
@@ -86,12 +95,25 @@ typedef struct {
|
||||
SDL_GameController *controller;
|
||||
SDL_GameController *controller_gyros;
|
||||
SDL_Texture *game_tex;
|
||||
#ifdef ANDROID
|
||||
SDL_Texture *android_video_tex;
|
||||
float android_video_transform[16];
|
||||
#endif
|
||||
int last_shown_toast;
|
||||
SDL_Texture *toast_tex;
|
||||
struct timeval toast_expiry;
|
||||
AVFrame *frame;
|
||||
AVFrame *held_frame; // keeps the displayed dmabuf alive until the next frame replaces it
|
||||
uint64_t present_frame_sequence;
|
||||
Uint64 update_tick_origin;
|
||||
Uint64 update_tick_frequency;
|
||||
uint64_t update_tick;
|
||||
#ifdef VANILLA_DRM_AVAILABLE
|
||||
int fast_drm_active;
|
||||
#endif
|
||||
#ifdef ANDROID
|
||||
int android_video_ready;
|
||||
#endif
|
||||
SDL_Texture *pw_tex;
|
||||
|
||||
uint8_t audio_buffer[AUDIO_BUFFER_COUNT][AUDIO_BUFFER_SIZE];
|
||||
@@ -685,6 +707,15 @@ int vui_sdl_event_thread(void *data)
|
||||
case SDL_TEXTEDITING:
|
||||
vpilog("text editing!\n");
|
||||
break;
|
||||
case SDL_APP_WILLENTERBACKGROUND:
|
||||
// On mobile, if the user is about to switch away from us,
|
||||
// disconnect from the console so it doesn't remain burning up
|
||||
// resources in the background
|
||||
if (vui_game_mode_get(vui)) {
|
||||
// Send shutdown signal
|
||||
vpi_game_shutdown();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
// }
|
||||
@@ -731,6 +762,14 @@ int vui_init_sdl(vui_context_t *ctx, int fullscreen)
|
||||
// SDL_SetHintWithPriority("SDL_GAMECONTROLLER_ALLOW_STEAM_VIRTUAL_GAMEPAD", "0", SDL_HINT_OVERRIDE);
|
||||
SDL_SetHintWithPriority(SDL_HINT_GAMECONTROLLER_IGNORE_DEVICES, "", SDL_HINT_OVERRIDE);
|
||||
|
||||
#ifdef ANDROID
|
||||
// Force SDL to use landscape orientations only
|
||||
SDL_SetHint(SDL_HINT_ORIENTATIONS, "LandscapeLeft LandscapeRight");
|
||||
|
||||
// Force opengles2 for hardware decode interoperability
|
||||
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengles2");
|
||||
#endif
|
||||
|
||||
// Force EGL when using X11 so that VAAPI works
|
||||
SDL_SetHint(SDL_HINT_VIDEO_X11_FORCE_EGL, "1");
|
||||
|
||||
@@ -899,6 +938,13 @@ int vui_init_sdl(vui_context_t *ctx, int fullscreen)
|
||||
|
||||
sdl_ctx->game_tex = 0;
|
||||
|
||||
#ifdef ANDROID
|
||||
sdl_ctx->android_video_tex = NULL;
|
||||
if (vui_sdl_android_create_video_texture(sdl_ctx->renderer, ctx->screen_width, ctx->screen_height, &sdl_ctx->android_video_tex) < 0) {
|
||||
vpilog("Android hardware decoding unavailable, falling back to software\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
sdl_ctx->background = 0;
|
||||
sdl_ctx->last_shown_toast = 0;
|
||||
sdl_ctx->toast_tex = 0;
|
||||
@@ -963,6 +1009,13 @@ void vui_close_sdl(vui_context_t *ctx)
|
||||
}
|
||||
|
||||
SDL_DestroyTexture(sdl_ctx->toast_tex);
|
||||
#ifdef ANDROID
|
||||
vui_sdl_android_destroy_video_texture();
|
||||
if (sdl_ctx->game_tex == sdl_ctx->android_video_tex) {
|
||||
sdl_ctx->game_tex = 0;
|
||||
}
|
||||
SDL_DestroyTexture(sdl_ctx->android_video_tex);
|
||||
#endif
|
||||
if (sdl_ctx->game_tex) SDL_DestroyTexture(sdl_ctx->game_tex);
|
||||
SDL_DestroyTexture(sdl_ctx->background);
|
||||
|
||||
@@ -1296,14 +1349,21 @@ void vui_sdl_draw_background(vui_context_t *ctx, SDL_Renderer *renderer)
|
||||
}
|
||||
}
|
||||
|
||||
void vui_draw_sdl(vui_context_t *ctx, SDL_Renderer *renderer)
|
||||
static SDL_Texture *vui_sdl_get_layer_texture(vui_context_t *ctx,
|
||||
SDL_Renderer *renderer,
|
||||
int layer)
|
||||
{
|
||||
vui_sdl_context_t *sdl_ctx = (vui_sdl_context_t *) ctx->platform_data;
|
||||
if (sdl_ctx->layer_data[layer]) {
|
||||
return sdl_ctx->layer_data[layer];
|
||||
}
|
||||
|
||||
for (int layer = 0; layer < ctx->layers; layer++) {
|
||||
sdl_ctx->layer_data[layer] = SDL_CreateTexture(
|
||||
renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_TARGET,
|
||||
ctx->screen_width, ctx->screen_height);
|
||||
if (!sdl_ctx->layer_data[layer]) {
|
||||
// Create a new layer here
|
||||
sdl_ctx->layer_data[layer] = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_TARGET, ctx->screen_width, ctx->screen_height);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SDL_BlendMode bm = SDL_ComposeCustomBlendMode(
|
||||
SDL_BLENDFACTOR_ONE,
|
||||
@@ -1314,10 +1374,18 @@ void vui_draw_sdl(vui_context_t *ctx, SDL_Renderer *renderer)
|
||||
SDL_BLENDOPERATION_ADD
|
||||
);
|
||||
|
||||
SDL_SetRenderDrawBlendMode(sdl_ctx->renderer, bm);
|
||||
SDL_SetRenderDrawBlendMode(renderer, bm);
|
||||
SDL_SetTextureBlendMode(sdl_ctx->layer_data[layer], bm);
|
||||
return sdl_ctx->layer_data[layer];
|
||||
}
|
||||
SDL_SetRenderTarget(renderer, sdl_ctx->layer_data[layer]);
|
||||
|
||||
void vui_draw_sdl(vui_context_t *ctx, SDL_Renderer *renderer)
|
||||
{
|
||||
vui_sdl_context_t *sdl_ctx = (vui_sdl_context_t *) ctx->platform_data;
|
||||
|
||||
for (int layer = 0; layer < ctx->layers; layer++) {
|
||||
SDL_Texture *layer_texture = vui_sdl_get_layer_texture(ctx, renderer, layer);
|
||||
SDL_SetRenderTarget(renderer, layer_texture);
|
||||
|
||||
if (layer == 0 && ctx->background_enabled) {
|
||||
// Draw background
|
||||
@@ -1640,6 +1708,15 @@ void vui_draw_sdl(vui_context_t *ctx, SDL_Renderer *renderer)
|
||||
|
||||
int get_texture_from_cpu_frame(vui_sdl_context_t *sdl_ctx, AVFrame *f)
|
||||
{
|
||||
#ifdef ANDROID
|
||||
// If we were using the Android texture, but for some reason switched to
|
||||
// software decoding, ensure our game_tex gets correctly recreated
|
||||
if (sdl_ctx->game_tex == sdl_ctx->android_video_tex) {
|
||||
sdl_ctx->game_tex = NULL;
|
||||
sdl_ctx->android_video_ready = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!sdl_ctx->game_tex) {
|
||||
sdl_ctx->game_tex = SDL_CreateTexture(
|
||||
sdl_ctx->renderer,
|
||||
@@ -1881,20 +1958,108 @@ int get_texture_from_drm_prime_frame(vui_sdl_context_t *sdl_ctx, AVFrame *f)
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ANDROID
|
||||
static SDL_FPoint transform_android_texcoord(const float matrix[16],
|
||||
float s, float t)
|
||||
{
|
||||
// Flip Y
|
||||
t = 1.0f - t;
|
||||
SDL_FPoint point = {
|
||||
matrix[0] * s + matrix[4] * t + matrix[12],
|
||||
matrix[1] * s + matrix[5] * t + matrix[13]
|
||||
};
|
||||
return point;
|
||||
}
|
||||
|
||||
static int render_android_video_texture(vui_sdl_context_t *sdl_ctx,
|
||||
int width, int height)
|
||||
{
|
||||
const float *matrix = sdl_ctx->android_video_transform;
|
||||
const SDL_Color white = { 255, 255, 255, 255 };
|
||||
SDL_Vertex vertices[4] = {
|
||||
{ { 0.0f, 0.0f }, white, transform_android_texcoord(matrix, 0.0f, 0.0f) },
|
||||
{ { (float) width, 0.0f }, white, transform_android_texcoord(matrix, 1.0f, 0.0f) },
|
||||
{ { (float) width, (float) height }, white, transform_android_texcoord(matrix, 1.0f, 1.0f) },
|
||||
{ { 0.0f, (float) height }, white, transform_android_texcoord(matrix, 0.0f, 1.0f) }
|
||||
};
|
||||
static const int indices[] = { 0, 1, 2, 0, 2, 3 };
|
||||
|
||||
if (SDL_RenderGeometry(sdl_ctx->renderer, sdl_ctx->android_video_tex, vertices, 4, indices, 6) < 0) {
|
||||
vpilog("Failed to render Android external texture: %s\n", SDL_GetError());
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
static Uint64 vui_sdl_tick_deadline(const vui_sdl_context_t *sdl_ctx,
|
||||
uint64_t tick)
|
||||
{
|
||||
Uint64 whole_seconds = tick / VUI_INPUT_TICK_RATE;
|
||||
Uint64 partial_ticks = tick % VUI_INPUT_TICK_RATE;
|
||||
return sdl_ctx->update_tick_origin
|
||||
+ whole_seconds * sdl_ctx->update_tick_frequency
|
||||
+ partial_ticks * sdl_ctx->update_tick_frequency
|
||||
/ VUI_INPUT_TICK_RATE;
|
||||
}
|
||||
|
||||
static void vui_sdl_wait_for_next_tick(vui_sdl_context_t *sdl_ctx)
|
||||
{
|
||||
sdl_ctx->update_tick++;
|
||||
|
||||
Uint64 now = SDL_GetPerformanceCounter();
|
||||
Uint64 deadline = vui_sdl_tick_deadline(sdl_ctx, sdl_ctx->update_tick);
|
||||
if (now >= deadline) {
|
||||
Uint64 elapsed = now - sdl_ctx->update_tick_origin;
|
||||
uint64_t elapsed_ticks =
|
||||
(elapsed / sdl_ctx->update_tick_frequency) * VUI_INPUT_TICK_RATE
|
||||
+ (elapsed % sdl_ctx->update_tick_frequency)
|
||||
* VUI_INPUT_TICK_RATE / sdl_ctx->update_tick_frequency;
|
||||
sdl_ctx->update_tick = elapsed_ticks + 1;
|
||||
deadline = vui_sdl_tick_deadline(sdl_ctx, sdl_ctx->update_tick);
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&vpi_present_frame_mutex);
|
||||
while ((now = SDL_GetPerformanceCounter()) < deadline) {
|
||||
Uint64 remaining = deadline - now;
|
||||
uint64_t wait_ns = remaining * 1000000000ULL
|
||||
/ sdl_ctx->update_tick_frequency;
|
||||
if (!wait_ns)
|
||||
wait_ns = 1;
|
||||
|
||||
struct timespec timeout;
|
||||
clock_gettime(CLOCK_REALTIME, &timeout);
|
||||
timeout.tv_sec += wait_ns / 1000000000ULL;
|
||||
timeout.tv_nsec += wait_ns % 1000000000ULL;
|
||||
if (timeout.tv_nsec >= 1000000000L) {
|
||||
timeout.tv_sec++;
|
||||
timeout.tv_nsec -= 1000000000L;
|
||||
}
|
||||
|
||||
pthread_cond_timedwait(&vpi_present_frame_cond, &vpi_present_frame_mutex, &timeout);
|
||||
}
|
||||
pthread_mutex_unlock(&vpi_present_frame_mutex);
|
||||
}
|
||||
|
||||
// Rendering/main thread
|
||||
int vui_update_sdl(vui_context_t *vui)
|
||||
{
|
||||
static Uint32 last_update_time = 0;
|
||||
|
||||
vui_sdl_event_thread(vui);
|
||||
|
||||
vui_sdl_context_t *sdl_ctx = (vui_sdl_context_t *) vui->platform_data;
|
||||
|
||||
if (!sdl_ctx->update_tick_frequency) {
|
||||
sdl_ctx->update_tick_frequency = SDL_GetPerformanceFrequency();
|
||||
sdl_ctx->update_tick_origin = SDL_GetPerformanceCounter();
|
||||
}
|
||||
int render_tick = sdl_ctx->update_tick % VUI_RENDER_TICK_DIVISOR == 0;
|
||||
|
||||
SDL_Renderer *renderer = sdl_ctx->renderer;
|
||||
|
||||
vui_update(vui);
|
||||
|
||||
SDL_Texture *main_tex;
|
||||
SDL_Texture *main_tex = NULL;
|
||||
|
||||
#ifdef VANILLA_DRM_AVAILABLE
|
||||
static vanilla_drm_ctx_t *drm_ctx = NULL;
|
||||
@@ -1903,13 +2068,14 @@ int vui_update_sdl(vui_context_t *vui)
|
||||
static vui_cuda_context_t *cuda_ctx = NULL;
|
||||
#endif // VANILLA_CUDA_AVAILABLE
|
||||
|
||||
int handle_final_blit = 1;
|
||||
int sdl_frame_ready = 0;
|
||||
if (!vui->game_mode) {
|
||||
|
||||
#ifdef VANILLA_DRM_AVAILABLE
|
||||
if (drm_ctx) {
|
||||
vui_sdl_drm_free(&drm_ctx); // will set to null
|
||||
}
|
||||
sdl_ctx->fast_drm_active = 0;
|
||||
#endif // VANILLA_DRM_AVAILABLE
|
||||
#ifdef VANILLA_CUDA_AVAILABLE
|
||||
if (cuda_ctx) {
|
||||
@@ -1920,6 +2086,33 @@ int vui_update_sdl(vui_context_t *vui)
|
||||
}
|
||||
#endif // VANILLA_CUDA_AVAILABLE
|
||||
|
||||
if (render_tick) {
|
||||
// Draw vui to a custom texture
|
||||
vui_draw_sdl(vui, renderer);
|
||||
|
||||
// Flatten layers
|
||||
int el[MAX_BUTTON_COUNT];
|
||||
int el_count = 0;
|
||||
for (int i = 0; i < vui->layers; i++) {
|
||||
if (vui->layer_enabled[i]) {
|
||||
el[el_count] = i;
|
||||
el_count++;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = el_count - 1; i > 0; i--) {
|
||||
SDL_Texture *bg = sdl_ctx->layer_data[el[i-1]];
|
||||
SDL_Texture *fg = sdl_ctx->layer_data[el[i]];
|
||||
|
||||
SDL_SetRenderTarget(renderer, bg);
|
||||
SDL_SetTextureColorMod(fg, vui->layer_opacity[i] * 0xFF, vui->layer_opacity[i] * 0xFF, vui->layer_opacity[i] * 0xFF);
|
||||
SDL_SetTextureAlphaMod(fg, vui->layer_opacity[i] * 0xFF);
|
||||
SDL_RenderCopy(renderer, fg, NULL, NULL);
|
||||
}
|
||||
|
||||
main_tex = sdl_ctx->layer_data[0];
|
||||
sdl_frame_ready = main_tex != NULL;
|
||||
}
|
||||
} else {
|
||||
pthread_mutex_lock(&vpi_present_frame_mutex);
|
||||
if (vpi_present_frame
|
||||
@@ -1950,14 +2143,15 @@ int vui_update_sdl(vui_context_t *vui)
|
||||
SDL_RenderPresent(renderer);
|
||||
}
|
||||
}
|
||||
if (drm_ctx &&
|
||||
vui_sdl_drm_present(drm_ctx, sdl_ctx->frame)) {
|
||||
handle_final_blit = 0;
|
||||
if (drm_ctx && vui_sdl_drm_present(drm_ctx, sdl_ctx->frame)) {
|
||||
sdl_ctx->fast_drm_active = 1;
|
||||
} else {
|
||||
sdl_ctx->fast_drm_active = 0;
|
||||
get_texture_from_drm_prime_frame(sdl_ctx,
|
||||
sdl_ctx->frame);
|
||||
}
|
||||
} else {
|
||||
sdl_ctx->fast_drm_active = 0;
|
||||
get_texture_from_drm_prime_frame(sdl_ctx, sdl_ctx->frame);
|
||||
}
|
||||
#endif // VANILLA_DRM_AVAILABLE
|
||||
@@ -1991,6 +2185,26 @@ int vui_update_sdl(vui_context_t *vui)
|
||||
}
|
||||
break;
|
||||
}
|
||||
#ifdef ANDROID
|
||||
case AV_PIX_FMT_MEDIACODEC:
|
||||
{
|
||||
AVMediaCodecBuffer *buffer = (AVMediaCodecBuffer *) sdl_ctx->frame->data[3];
|
||||
if (!sdl_ctx->android_video_tex || !buffer) {
|
||||
vpilog("MediaCodec produced a frame without an Android video surface\n");
|
||||
break;
|
||||
}
|
||||
if (sdl_ctx->game_tex && sdl_ctx->game_tex != sdl_ctx->android_video_tex) {
|
||||
SDL_DestroyTexture(sdl_ctx->game_tex);
|
||||
}
|
||||
sdl_ctx->game_tex = sdl_ctx->android_video_tex;
|
||||
sdl_ctx->android_video_ready = 0;
|
||||
if (av_mediacodec_release_buffer(buffer, 1) < 0) {
|
||||
vpilog("Failed to render MediaCodec output buffer\n");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case AV_PIX_FMT_YUV420P:
|
||||
get_texture_from_cpu_frame(sdl_ctx, sdl_ctx->frame);
|
||||
break;
|
||||
@@ -1998,59 +2212,51 @@ int vui_update_sdl(vui_context_t *vui)
|
||||
|
||||
av_frame_unref(sdl_ctx->held_frame);
|
||||
av_frame_move_ref(sdl_ctx->held_frame, sdl_ctx->frame);
|
||||
}
|
||||
|
||||
#ifdef ANDROID
|
||||
/* MediaCodec publishes into SurfaceTexture asynchronously. Poll it at
|
||||
* the input rate, but only draw the retained image on render ticks. */
|
||||
if (sdl_ctx->game_tex == sdl_ctx->android_video_tex
|
||||
&& SDL_RenderFlush(renderer) == 0
|
||||
&& vui_sdl_android_update_video_texture(
|
||||
sdl_ctx->android_video_transform)) {
|
||||
sdl_ctx->android_video_ready = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
int sdl_handles_final_blit = 1;
|
||||
#ifdef VANILLA_DRM_AVAILABLE
|
||||
sdl_handles_final_blit = !sdl_ctx->fast_drm_active;
|
||||
#endif
|
||||
if (render_tick && sdl_handles_final_blit) {
|
||||
main_tex = vui_sdl_get_layer_texture(vui, renderer, 0);
|
||||
if (main_tex) {
|
||||
SDL_SetRenderTarget(renderer, main_tex);
|
||||
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
|
||||
SDL_RenderClear(renderer);
|
||||
|
||||
if (sdl_ctx->game_tex) {
|
||||
if (handle_final_blit) {
|
||||
main_tex = sdl_ctx->layer_data[0];
|
||||
SDL_SetRenderTarget(renderer, main_tex);
|
||||
SDL_RenderCopy(renderer, sdl_ctx->game_tex, 0, 0);
|
||||
} else {
|
||||
main_tex = sdl_ctx->game_tex;
|
||||
#ifdef ANDROID
|
||||
if (sdl_ctx->game_tex == sdl_ctx->android_video_tex) {
|
||||
if (sdl_ctx->android_video_ready) {
|
||||
render_android_video_texture(
|
||||
sdl_ctx, vui->screen_width,
|
||||
vui->screen_height);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Didn't get a frame, nothing to be done
|
||||
handle_final_blit = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (handle_final_blit) {
|
||||
// Draw vui to a custom texture
|
||||
if (!vui->game_mode) {
|
||||
vui_draw_sdl(vui, renderer);
|
||||
|
||||
// Flatten layers
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
int el[MAX_BUTTON_COUNT];
|
||||
int el_count = 0;
|
||||
for (int i = 0; i < vui->layers; i++) {
|
||||
if (vui->layer_enabled[i]) {
|
||||
el[el_count] = i;
|
||||
el_count++;
|
||||
SDL_RenderCopy(renderer, sdl_ctx->game_tex, NULL,
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = el_count - 1; i > 0; i--) {
|
||||
SDL_Texture *bg = sdl_ctx->layer_data[el[i-1]];
|
||||
SDL_Texture *fg = sdl_ctx->layer_data[el[i]];
|
||||
|
||||
SDL_SetRenderTarget(renderer, bg);
|
||||
SDL_SetTextureColorMod(fg, vui->layer_opacity[i] * 0xFF, vui->layer_opacity[i] * 0xFF, vui->layer_opacity[i] * 0xFF);
|
||||
SDL_SetTextureAlphaMod(fg, vui->layer_opacity[i] * 0xFF);
|
||||
SDL_RenderCopy(renderer, fg, NULL, NULL);
|
||||
}
|
||||
|
||||
if (vui->game_mode) {
|
||||
// TODO: Composite onto game texture
|
||||
// SDL_SetRenderTarget(renderer, main_tex);
|
||||
// SDL_RenderCopy(renderer, sdl_ctx->layer_data[0], NULL, NULL);
|
||||
} else {
|
||||
// Just return our texture
|
||||
main_tex = sdl_ctx->layer_data[0];
|
||||
sdl_frame_ready = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sdl_frame_ready) {
|
||||
// Draw toast on screen if necessary
|
||||
{
|
||||
const int TOAST_PADDING = 12;
|
||||
@@ -2159,37 +2365,9 @@ int vui_update_sdl(vui_context_t *vui)
|
||||
SDL_RenderPresent(renderer);
|
||||
}
|
||||
|
||||
// Frame limiter to save CPU cycles
|
||||
const Uint32 target = 5; // No need to update faster than 200Hz (gamepad polls at 180Hz, but this is easier to calculate)
|
||||
Uint32 frame_delta = SDL_GetTicks() - last_update_time;
|
||||
if (frame_delta < target) {
|
||||
Uint32 wait_ms = target - frame_delta;
|
||||
if (vui->game_mode) {
|
||||
struct timespec deadline;
|
||||
clock_gettime(CLOCK_REALTIME, &deadline);
|
||||
deadline.tv_nsec += (long) wait_ms * 1000000;
|
||||
if (deadline.tv_nsec >= 1000000000) {
|
||||
deadline.tv_sec++;
|
||||
deadline.tv_nsec -= 1000000000;
|
||||
if (!vui->quit) {
|
||||
vui_sdl_wait_for_next_tick(sdl_ctx);
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&vpi_present_frame_mutex);
|
||||
while (sdl_ctx->present_frame_sequence == vpi_present_frame_sequence) {
|
||||
int err = pthread_cond_timedwait(
|
||||
&vpi_present_frame_cond,
|
||||
&vpi_present_frame_mutex,
|
||||
&deadline
|
||||
);
|
||||
if (err == ETIMEDOUT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&vpi_present_frame_mutex);
|
||||
} else {
|
||||
SDL_Delay(wait_ms);
|
||||
}
|
||||
}
|
||||
last_update_time = SDL_GetTicks();
|
||||
|
||||
return !vui->quit;
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
#include "ui_sdl_android.h"
|
||||
|
||||
#include <jni.h>
|
||||
#include <SDL2/SDL_opengles2.h>
|
||||
#include <SDL2/SDL_system.h>
|
||||
#include <libavcodec/jni.h>
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
static jclass video_class;
|
||||
static jobject video_surface;
|
||||
static jmethodID create_surface_method;
|
||||
static jmethodID update_texture_method;
|
||||
static jmethodID destroy_surface_method;
|
||||
|
||||
static int handle_jni_exception(JNIEnv *env, const char *operation)
|
||||
{
|
||||
if (!(*env)->ExceptionCheck(env)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
(*env)->ExceptionDescribe(env);
|
||||
(*env)->ExceptionClear(env);
|
||||
vpilog("Android video surface: %s failed\n", operation);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int init_jni(JNIEnv *env)
|
||||
{
|
||||
if (video_class) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
jobject activity = (jobject) SDL_AndroidGetActivity();
|
||||
if (!activity) {
|
||||
vpilog("SDL_AndroidGetActivity returned NULL\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
jclass local_class = (*env)->GetObjectClass(env, activity);
|
||||
(*env)->DeleteLocalRef(env, activity);
|
||||
if (!local_class || handle_jni_exception(env, "looking up activity class") < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
video_class = (jclass) (*env)->NewGlobalRef(env, local_class);
|
||||
(*env)->DeleteLocalRef(env, local_class);
|
||||
if (!video_class) {
|
||||
vpilog("Android video surface: could not retain activity class\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
create_surface_method = (*env)->GetStaticMethodID(env, video_class, "createVideoSurface", "(III)Landroid/view/Surface;");
|
||||
update_texture_method = (*env)->GetStaticMethodID(env, video_class, "updateVideoSurfaceTexture", "()[F");
|
||||
destroy_surface_method = (*env)->GetStaticMethodID(env, video_class, "destroyVideoSurface", "()V");
|
||||
if (!create_surface_method || !update_texture_method || !destroy_surface_method ||
|
||||
handle_jni_exception(env, "looking up video surface methods") < 0) {
|
||||
(*env)->DeleteGlobalRef(env, video_class);
|
||||
video_class = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
JavaVM *vm = NULL;
|
||||
if ((*env)->GetJavaVM(env, &vm) != JNI_OK ||
|
||||
av_jni_set_java_vm(vm, NULL) < 0) {
|
||||
vpilog("Android video surface: could not register the Java VM with FFmpeg\n");
|
||||
(*env)->DeleteGlobalRef(env, video_class);
|
||||
video_class = NULL;
|
||||
create_surface_method = NULL;
|
||||
update_texture_method = NULL;
|
||||
destroy_surface_method = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vui_sdl_android_create_video_texture(SDL_Renderer *renderer, int width, int height, SDL_Texture **texture)
|
||||
{
|
||||
JNIEnv *env = (JNIEnv *) SDL_AndroidGetJNIEnv();
|
||||
SDL_RendererInfo info;
|
||||
GLint texture_name = 0;
|
||||
SDL_Texture *external_texture = NULL;
|
||||
|
||||
if (!env || !renderer || !texture || init_jni(env) < 0) {
|
||||
goto die;
|
||||
}
|
||||
|
||||
if (SDL_GetRendererInfo(renderer, &info) < 0) {
|
||||
vpilog("Failed to determine SDL Android renderer info\n");
|
||||
goto die;
|
||||
}
|
||||
|
||||
if (SDL_strcmp(info.name, "opengles2") != 0) {
|
||||
vpilog("Android video decoding requires opengles2 (currently using %s)\n", info.name);
|
||||
goto die;
|
||||
}
|
||||
|
||||
external_texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_EXTERNAL_OES, SDL_TEXTUREACCESS_STATIC, width, height);
|
||||
if (!external_texture) {
|
||||
vpilog("Failed to create Android external texture: %s\n", SDL_GetError());
|
||||
goto die;
|
||||
}
|
||||
|
||||
SDL_RenderFlush(renderer);
|
||||
if (SDL_GL_BindTexture(external_texture, NULL, NULL) < 0) {
|
||||
vpilog("Failed to bind Android external texture: %s\n", SDL_GetError());
|
||||
goto die_and_destroy_texture;
|
||||
}
|
||||
|
||||
glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &texture_name);
|
||||
SDL_GL_UnbindTexture(external_texture);
|
||||
if (!texture_name) {
|
||||
vpilog("Failed to retrieve GL object from Android external texture\n");
|
||||
goto die_and_destroy_texture;
|
||||
}
|
||||
|
||||
jobject local_surface = (*env)->CallStaticObjectMethod(env, video_class, create_surface_method, (jint) texture_name, (jint) width, (jint) height);
|
||||
if (handle_jni_exception(env, "creating SurfaceTexture") < 0 || !local_surface) {
|
||||
goto die_and_destroy_texture;
|
||||
}
|
||||
|
||||
video_surface = (*env)->NewGlobalRef(env, local_surface);
|
||||
(*env)->DeleteLocalRef(env, local_surface);
|
||||
if (!video_surface) {
|
||||
(*env)->CallStaticVoidMethod(env, video_class, destroy_surface_method);
|
||||
handle_jni_exception(env, "destroying unretained SurfaceTexture");
|
||||
goto die_and_destroy_texture;
|
||||
}
|
||||
|
||||
*texture = external_texture;
|
||||
vpilog("Android hardware video decode surface initialized\n");
|
||||
return 0;
|
||||
|
||||
die_and_destroy_texture:
|
||||
SDL_DestroyTexture(external_texture);
|
||||
|
||||
die:
|
||||
return -1;
|
||||
}
|
||||
|
||||
void *vui_sdl_android_get_video_surface(void)
|
||||
{
|
||||
return video_surface;
|
||||
}
|
||||
|
||||
int vui_sdl_android_update_video_texture(float transform[16])
|
||||
{
|
||||
JNIEnv *env = (JNIEnv *) SDL_AndroidGetJNIEnv();
|
||||
if (!env || !video_class || !video_surface) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
jfloatArray matrix = (jfloatArray) (*env)->CallStaticObjectMethod(env, video_class, update_texture_method);
|
||||
if (handle_jni_exception(env, "updating SurfaceTexture") < 0 || !matrix) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((*env)->GetArrayLength(env, matrix) != 16) {
|
||||
(*env)->DeleteLocalRef(env, matrix);
|
||||
vpilog("Android video surface returned an invalid transform matrix\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
(*env)->GetFloatArrayRegion(env, matrix, 0, 16, transform);
|
||||
(*env)->DeleteLocalRef(env, matrix);
|
||||
if (handle_jni_exception(env, "reading SurfaceTexture transform") < 0) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void vui_sdl_android_destroy_video_texture(void)
|
||||
{
|
||||
JNIEnv *env = (JNIEnv *) SDL_AndroidGetJNIEnv();
|
||||
if (!env) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (video_class && destroy_surface_method) {
|
||||
(*env)->CallStaticVoidMethod(env, video_class, destroy_surface_method);
|
||||
handle_jni_exception(env, "destroying SurfaceTexture");
|
||||
}
|
||||
if (video_surface) {
|
||||
(*env)->DeleteGlobalRef(env, video_surface);
|
||||
video_surface = NULL;
|
||||
}
|
||||
if (video_class) {
|
||||
(*env)->DeleteGlobalRef(env, video_class);
|
||||
video_class = NULL;
|
||||
}
|
||||
create_surface_method = NULL;
|
||||
update_texture_method = NULL;
|
||||
destroy_surface_method = NULL;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#ifndef VANILLA_UI_SDL_ANDROID_H
|
||||
#define VANILLA_UI_SDL_ANDROID_H
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
int vui_sdl_android_create_video_texture(SDL_Renderer *renderer, int width,
|
||||
int height, SDL_Texture **texture);
|
||||
void *vui_sdl_android_get_video_surface(void);
|
||||
int vui_sdl_android_update_video_texture(float transform[16]);
|
||||
void vui_sdl_android_destroy_video_texture(void);
|
||||
|
||||
#endif
|
||||
+43
-41
@@ -37,38 +37,24 @@ static inline int skterr()
|
||||
#endif
|
||||
}
|
||||
|
||||
void create_sockaddr(sockaddr_u *addr, size_t *size, in_addr_t inaddr, uint16_t port, int local, int delete)
|
||||
static void create_sockaddr(struct sockaddr_in *addr, size_t *size, in_addr_t inaddr, uint16_t port)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
if (local) {
|
||||
memset(&addr->un, 0, sizeof(addr->un));
|
||||
addr->un.sun_family = AF_UNIX;
|
||||
snprintf(addr->un.sun_path, sizeof(addr->un.sun_path) - 1, VANILLA_PIPE_LOCAL_SOCKET, port);
|
||||
if (delete)
|
||||
unlink(addr->un.sun_path);
|
||||
memset(addr, 0, sizeof(*addr));
|
||||
addr->sin_family = AF_INET;
|
||||
addr->sin_port = htons(port);
|
||||
addr->sin_addr.s_addr = inaddr;
|
||||
|
||||
if (size) *size = sizeof(struct sockaddr_un);
|
||||
} else {
|
||||
#endif
|
||||
memset(&addr->in, 0, sizeof(addr->in));
|
||||
addr->in.sin_family = AF_INET;
|
||||
addr->in.sin_port = htons(port);
|
||||
addr->in.sin_addr.s_addr = inaddr;
|
||||
|
||||
if (size) *size = sizeof(struct sockaddr_in);
|
||||
#ifndef _WIN32
|
||||
}
|
||||
#endif
|
||||
if (size) *size = sizeof(*addr);
|
||||
}
|
||||
|
||||
void create_server_sockaddr(sockaddr_u *addr, size_t *size, uint16_t port, int delete)
|
||||
void create_server_sockaddr(struct sockaddr_in *addr, size_t *size, uint16_t port)
|
||||
{
|
||||
// The Wii U always places itself at this address
|
||||
in_addr_t ip = (SERVER_ADDRESS == VANILLA_ADDRESS_LOCAL) ? inet_addr("192.168.1.10") : SERVER_ADDRESS;
|
||||
return create_sockaddr(addr, size, ip, port, 0, delete);
|
||||
create_sockaddr(addr, size, ip, port);
|
||||
}
|
||||
|
||||
void send_to_sockaddr(int fd, const void *data, size_t data_size, const sockaddr_u *sockaddr, size_t sockaddr_size)
|
||||
void send_to_sockaddr(int fd, const void *data, size_t data_size, const struct sockaddr_in *sockaddr, size_t sockaddr_size)
|
||||
{
|
||||
ssize_t sent = sendto(fd, data, data_size, 0, (const struct sockaddr *) sockaddr, sockaddr_size);
|
||||
if (sent == -1) {
|
||||
@@ -81,12 +67,12 @@ void send_to_sockaddr(int fd, const void *data, size_t data_size, const sockaddr
|
||||
|
||||
void send_to_console(int fd, const void *data, size_t data_size, uint16_t port)
|
||||
{
|
||||
sockaddr_u addr;
|
||||
struct sockaddr_in addr;
|
||||
size_t addr_size;
|
||||
|
||||
in_port_t console_port = port - 100;
|
||||
|
||||
create_server_sockaddr(&addr, &addr_size, console_port, 0);
|
||||
create_server_sockaddr(&addr, &addr_size, console_port);
|
||||
|
||||
send_to_sockaddr(fd, data, data_size, &addr, addr_size);
|
||||
}
|
||||
@@ -106,15 +92,14 @@ void set_socket_rcvtimeo(int skt, uint64_t microseconds)
|
||||
|
||||
int create_socket(int *socket_out, in_port_t port, int pipe)
|
||||
{
|
||||
sockaddr_u addr;
|
||||
struct sockaddr_in addr;
|
||||
size_t addr_size;
|
||||
|
||||
int is_pipe_and_local = (pipe && SERVER_ADDRESS == VANILLA_ADDRESS_LOCAL);
|
||||
int domain = is_pipe_and_local ? AF_UNIX : AF_INET;
|
||||
in_addr_t bind_address = (pipe && SERVER_ADDRESS == VANILLA_ADDRESS_LOCAL) ? htonl(INADDR_LOOPBACK) : INADDR_ANY;
|
||||
|
||||
create_sockaddr(&addr, &addr_size, INADDR_ANY, port, is_pipe_and_local, 1);
|
||||
create_sockaddr(&addr, &addr_size, bind_address, port);
|
||||
|
||||
int skt = socket(domain, SOCK_DGRAM, 0);
|
||||
int skt = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (skt == -1) {
|
||||
vanilla_log("FAILED TO CREATE SOCKET: %i", skterr());
|
||||
return VANILLA_ERR_BAD_SOCKET;
|
||||
@@ -128,21 +113,21 @@ int create_socket(int *socket_out, in_port_t port, int pipe)
|
||||
|
||||
// vanilla_log("SUCCESSFULLY BOUND SOCKET %i ON PORT %i", skt, port);
|
||||
|
||||
(*socket_out) = skt;
|
||||
|
||||
set_socket_rcvtimeo(skt, 250000);
|
||||
|
||||
// int buf_sz = 16777216;
|
||||
// setsockopt(skt, SOL_SOCKET, SO_RCVBUF, &buf_sz, sizeof(buf_sz));
|
||||
// setsockopt(skt, SOL_SOCKET, SO_SNDBUF, &buf_sz, sizeof(buf_sz));
|
||||
|
||||
#if !defined(_WIN32) && !defined(__APPLE__)
|
||||
#if !defined(_WIN32) && !defined(__APPLE__) && !defined(ANDROID)
|
||||
if (!pipe && SERVER_ADDRESS == VANILLA_ADDRESS_LOCAL) {
|
||||
// Bind to wireless device
|
||||
setsockopt(skt, SOL_SOCKET, SO_BINDTODEVICE, wireless_interface, strlen(wireless_interface));
|
||||
}
|
||||
#endif
|
||||
|
||||
(*socket_out) = skt;
|
||||
|
||||
// int val = 1000; // microseconds to busy-poll per recv() before sleeping
|
||||
// if (setsockopt(skt, SOL_SOCKET, SO_BUSY_POLL, &val, sizeof(val)) < 0) {
|
||||
// perror("setsockopt SO_BUSY_POLL");
|
||||
@@ -153,20 +138,19 @@ int create_socket(int *socket_out, in_port_t port, int pipe)
|
||||
|
||||
int send_pipe_cc(int skt, vanilla_pipe_command_t *cmd, size_t cmd_size, int wait_for_reply)
|
||||
{
|
||||
sockaddr_u addr;
|
||||
struct sockaddr_in addr;
|
||||
size_t addr_size;
|
||||
|
||||
int pipe_is_local = (SERVER_ADDRESS == VANILLA_ADDRESS_LOCAL);
|
||||
in_addr_t pipe_addr = pipe_is_local ? INADDR_ANY : SERVER_ADDRESS;
|
||||
in_addr_t pipe_addr = (SERVER_ADDRESS == VANILLA_ADDRESS_LOCAL) ? htonl(INADDR_LOOPBACK) : SERVER_ADDRESS;
|
||||
|
||||
create_sockaddr(&addr, &addr_size, pipe_addr, VANILLA_PIPE_CMD_SERVER_PORT, pipe_is_local, 0);
|
||||
create_sockaddr(&addr, &addr_size, pipe_addr, VANILLA_PIPE_CMD_SERVER_PORT);
|
||||
|
||||
ssize_t read_size;
|
||||
uint8_t recv_cc;
|
||||
|
||||
for (int retries = 0; retries < MAX_PIPE_RETRY; retries++) {
|
||||
if (sendto(skt, (const char *) cmd, cmd_size, 0, (const struct sockaddr *) &addr, addr_size) == -1) {
|
||||
vanilla_log("Failed to write control code to socket");
|
||||
vanilla_log("Failed to write control code to socket: %i", skterr());
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -175,11 +159,23 @@ int send_pipe_cc(int skt, vanilla_pipe_command_t *cmd, size_t cmd_size, int wait
|
||||
}
|
||||
|
||||
read_size = recv(skt, &recv_cc, sizeof(recv_cc), 0);
|
||||
if (read_size == sizeof(recv_cc)) {
|
||||
if (recv_cc == VANILLA_PIPE_CC_BIND_ACK) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
vanilla_log("STILL WAITING FOR REPLY");
|
||||
if (recv_cc == VANILLA_PIPE_CC_BUSY) {
|
||||
if (retries == MAX_PIPE_RETRY - 1) {
|
||||
return -1;
|
||||
}
|
||||
vanilla_log("PIPE BUSY, RETRYING");
|
||||
} else {
|
||||
vanilla_log("UNEXPECTED PIPE REPLY: 0x%02x", recv_cc);
|
||||
}
|
||||
} else if (read_size < 0) {
|
||||
vanilla_log("STILL WAITING FOR REPLY: %i", skterr());
|
||||
} else {
|
||||
vanilla_log("UNEXPECTED PIPE REPLY SIZE: %zi", read_size);
|
||||
}
|
||||
|
||||
sleep(1);
|
||||
}
|
||||
@@ -205,7 +201,13 @@ int connect_to_backend(int *socket, vanilla_pipe_command_t *cmd, size_t cmd_size
|
||||
|
||||
set_socket_rcvtimeo(pipe_cc_skt, 2000000);
|
||||
|
||||
if (!send_pipe_cc(pipe_cc_skt, cmd, cmd_size, 1)) {
|
||||
int send_result = send_pipe_cc(pipe_cc_skt, cmd, cmd_size, 1);
|
||||
if (send_result < 0) {
|
||||
vanilla_log("PIPE IS BUSY");
|
||||
close(pipe_cc_skt);
|
||||
return VANILLA_ERR_BUSY;
|
||||
}
|
||||
if (!send_result) {
|
||||
vanilla_log("FAILED TO BIND TO PIPE");
|
||||
close(pipe_cc_skt);
|
||||
return VANILLA_ERR_PIPE_UNRESPONSIVE;
|
||||
|
||||
+2
-10
@@ -11,7 +11,6 @@ typedef uint16_t in_port_t;
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <sys/un.h>
|
||||
#endif
|
||||
|
||||
#include "vanilla.h"
|
||||
@@ -55,18 +54,11 @@ typedef struct thread_data_t
|
||||
vanilla_psk_t psk;
|
||||
} thread_data_t;
|
||||
|
||||
typedef union {
|
||||
struct sockaddr_in in;
|
||||
#ifndef _WIN32
|
||||
struct sockaddr_un un;
|
||||
#endif
|
||||
} sockaddr_u;
|
||||
|
||||
void sync_internal(thread_data_t *data);
|
||||
void connect_as_gamepad_internal(thread_data_t *data);
|
||||
int install_polkit_internal(thread_data_t *data, int install);
|
||||
void create_server_sockaddr(sockaddr_u *addr, size_t *size, uint16_t port, int delete);
|
||||
void send_to_sockaddr(int fd, const void *data, size_t data_size, const sockaddr_u *sockaddr, size_t sockaddr_size);
|
||||
void create_server_sockaddr(struct sockaddr_in *addr, size_t *size, uint16_t port);
|
||||
void send_to_sockaddr(int fd, const void *data, size_t data_size, const struct sockaddr_in *sockaddr, size_t sockaddr_size);
|
||||
void send_to_console(int fd, const void *data, size_t data_size, uint16_t port);
|
||||
int push_event(event_loop_t *loop, int type, const void *data, size_t size);
|
||||
int get_event(event_loop_t *loop, vanilla_event_t *event, int wait);
|
||||
|
||||
+3
-3
@@ -200,7 +200,7 @@ void set_battery_status(int status)
|
||||
pthread_mutex_unlock(&button_mtx);
|
||||
}
|
||||
|
||||
void send_input(int socket_hid, const sockaddr_u *addr, size_t addr_size)
|
||||
void send_input(int socket_hid, const struct sockaddr_in *addr, size_t addr_size)
|
||||
{
|
||||
InputPacket ip;
|
||||
memset(&ip, 0, sizeof(ip));
|
||||
@@ -298,9 +298,9 @@ void *listen_input(void *x)
|
||||
|
||||
pthread_mutex_init(&button_mtx, NULL);
|
||||
|
||||
sockaddr_u addr;
|
||||
struct sockaddr_in addr;
|
||||
size_t addr_size;
|
||||
create_server_sockaddr(&addr, &addr_size, PORT_HID - 100, 0);
|
||||
create_server_sockaddr(&addr, &addr_size, PORT_HID - 100);
|
||||
|
||||
do {
|
||||
send_input(info->socket_hid, &addr, addr_size);
|
||||
|
||||
@@ -20,8 +20,6 @@
|
||||
#define VANILLA_PIPE_CC_UNINSTALL_POLKIT 0x8B
|
||||
#define VANILLA_PIPE_CC_QUIT 0x90
|
||||
|
||||
#define VANILLA_PIPE_LOCAL_SOCKET "/tmp/vanilla-pipe_%i.sock"
|
||||
|
||||
#define POLKIT_ACTION_DST "/usr/share/polkit-1/actions/com.mattkc.vanilla.policy"
|
||||
#define POLKIT_RULE_DST "/usr/share/polkit-1/rules.d/com.mattkc.vanilla.rules"
|
||||
|
||||
|
||||
+67
-46
@@ -1,15 +1,31 @@
|
||||
include(ExternalProject)
|
||||
|
||||
# Add vanilla-pipe executalbe
|
||||
# Add vanilla-pipe executable
|
||||
add_executable(vanilla-pipe
|
||||
dhcp/dhcp.c
|
||||
main.c
|
||||
wpa.c
|
||||
)
|
||||
|
||||
if(ANDROID)
|
||||
target_sources(vanilla-pipe PRIVATE android.c)
|
||||
endif()
|
||||
|
||||
# Install vanilla-pipe
|
||||
install(TARGETS vanilla-pipe)
|
||||
|
||||
option(VANILLA_BUILD_USE_LIBNM "Build with support for communicating to NetworkManager" ON)
|
||||
# vanilla-pipe remains an executable, but is given a .so extension so Gradle will
|
||||
# place it alongside main executable libvanilla.so
|
||||
if(ANDROID)
|
||||
set_target_properties(vanilla-pipe PROPERTIES
|
||||
OUTPUT_NAME vanilla_pipe
|
||||
PREFIX "lib"
|
||||
SUFFIX ".so"
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}"
|
||||
)
|
||||
endif()
|
||||
|
||||
option(VANILLA_BUILD_USE_LIBNM "Build with support for communicating to NetworkManager" ${LINUX})
|
||||
if (VANILLA_BUILD_USE_LIBNM)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(LIBNM REQUIRED libnm)
|
||||
@@ -18,34 +34,27 @@ if (VANILLA_BUILD_USE_LIBNM)
|
||||
target_compile_definitions(vanilla-pipe PRIVATE USE_LIBNM)
|
||||
endif()
|
||||
|
||||
if (VANILLA_BUILD_VENDORED)
|
||||
# Build OpenSSL
|
||||
set(OPENSSL_DIR ${CMAKE_CURRENT_BINARY_DIR}/openssl)
|
||||
set(OPENSSL_INSTALL_DIR ${OPENSSL_DIR}/install)
|
||||
set(OPENSSL_PROCESSOR ${CMAKE_SYSTEM_PROCESSOR})
|
||||
set(OPENSSL_EXTRA_FLAGS "")
|
||||
if ("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "arm")
|
||||
set(OPENSSL_PROCESSOR "armv4")
|
||||
set(OPENSSL_EXTRA_FLAGS "${CMAKE_C_FLAGS}")
|
||||
endif()
|
||||
set(OPENSSL_LIBRARIES "${OPENSSL_DIR}/libssl.a" "${OPENSSL_DIR}/libcrypto.a")
|
||||
ExternalProject_Add(openssl
|
||||
SOURCE_DIR ${OPENSSL_DIR}
|
||||
BINARY_DIR ${OPENSSL_DIR}
|
||||
GIT_REPOSITORY https://github.com/openssl/openssl.git
|
||||
GIT_TAG openssl-3.5.0
|
||||
CONFIGURE_COMMAND env CC=${CMAKE_C_COMPILER} ./Configure --prefix=${OPENSSL_INSTALL_DIR} linux-${OPENSSL_PROCESSOR}
|
||||
INSTALL_COMMAND make install_sw
|
||||
UPDATE_COMMAND ""
|
||||
BUILD_BYPRODUCTS
|
||||
${OPENSSL_LIBRARIES}
|
||||
set(PIPE_TOOLCHAIN_FLAGS "")
|
||||
if(ANDROID)
|
||||
set(PIPE_TOOLCHAIN_FLAGS
|
||||
"--target=${ANDROID_LLVM_TRIPLE} --sysroot=${CMAKE_SYSROOT}"
|
||||
)
|
||||
add_dependencies(vanilla-pipe openssl)
|
||||
endif()
|
||||
|
||||
if (VANILLA_BUILD_VENDORED)
|
||||
# Build LibNL
|
||||
set(LIBNL_DIR ${CMAKE_CURRENT_BINARY_DIR}/libnl)
|
||||
set(LIBNL_INSTALL_DIR ${LIBNL_DIR}/install)
|
||||
if ("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "arm")
|
||||
set(LIBNL_EXTRA_FLAGS "")
|
||||
set(LIBNL_CFLAGS "${CMAKE_C_FLAGS} ${PIPE_TOOLCHAIN_FLAGS}")
|
||||
if(ANDROID)
|
||||
set(LIBNL_EXTRA_FLAGS --disable-pthreads)
|
||||
string(APPEND LIBNL_CFLAGS " -D__UAPI_DEF_IN_ADDR=0 -include bits/in_addr.h")
|
||||
endif()
|
||||
|
||||
if (ANDROID)
|
||||
set(LIBNL_HOST ${ANDROID_LLVM_TRIPLE})
|
||||
elseif ("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "arm")
|
||||
set(LIBNL_HOST arm-linux-gnueabihf)
|
||||
else()
|
||||
set(LIBNL_HOST ${CMAKE_SYSTEM_PROCESSOR}-linux-gnu)
|
||||
@@ -59,7 +68,14 @@ if (VANILLA_BUILD_VENDORED)
|
||||
BINARY_DIR ${LIBNL_DIR}
|
||||
GIT_REPOSITORY https://github.com/thom311/libnl.git
|
||||
GIT_TAG libnl3_11_0
|
||||
CONFIGURE_COMMAND autoreconf -i && ./configure --prefix=${LIBNL_INSTALL_DIR} --host=${LIBNL_HOST}
|
||||
PATCH_COMMAND autoreconf -i
|
||||
CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env
|
||||
CC=${CMAKE_C_COMPILER}
|
||||
AR=${CMAKE_AR}
|
||||
RANLIB=${CMAKE_RANLIB}
|
||||
"CFLAGS=${LIBNL_CFLAGS}"
|
||||
"LDFLAGS=${CMAKE_EXE_LINKER_FLAGS}"
|
||||
./configure --prefix=${LIBNL_INSTALL_DIR} --host=${LIBNL_HOST} --disable-shared --enable-static --disable-cli ${LIBNL_EXTRA_FLAGS}
|
||||
UPDATE_COMMAND ""
|
||||
BUILD_BYPRODUCTS
|
||||
${LibNL_LIBRARY}
|
||||
@@ -69,27 +85,33 @@ if (VANILLA_BUILD_VENDORED)
|
||||
)
|
||||
add_dependencies(vanilla-pipe libnl)
|
||||
else()
|
||||
# Find LibNL
|
||||
# Find system LibNL
|
||||
find_package(LibNL REQUIRED)
|
||||
|
||||
# Find OpenSSL and libnm
|
||||
find_package(OpenSSL REQUIRED)
|
||||
endif()
|
||||
|
||||
# Build WPA supplicant
|
||||
set(HOSTAP_DIR ${CMAKE_CURRENT_BINARY_DIR}/hostap)
|
||||
set(HOSTAP_CFLAGS "${PIPE_TOOLCHAIN_FLAGS}")
|
||||
set(HOSTAP_ENV
|
||||
CC=${CMAKE_C_COMPILER}
|
||||
AR=${CMAKE_AR}
|
||||
RANLIB=${CMAKE_RANLIB}
|
||||
CONFIG_TLS=internal
|
||||
CONFIG_CRYPTO=internal
|
||||
CONFIG_INTERNAL_LIBTOMMATH=y
|
||||
CONFIG_INTERNAL_LIBTOMMATH_FAST=y
|
||||
)
|
||||
if (VANILLA_BUILD_VENDORED)
|
||||
set(HOSTAP_CFLAGS "-I${LIBNL_INSTALL_DIR}/include/libnl3 -I${OPENSSL_INSTALL_DIR}/include")
|
||||
set(HOSTAP_LDFLAGS "-L${LIBNL_INSTALL_DIR}/lib -L${OPENSSL_INSTALL_DIR}/lib64 -L${OPENSSL_INSTALL_DIR}/lib")
|
||||
string(APPEND HOSTAP_CFLAGS " -I${LIBNL_INSTALL_DIR}/include/libnl3")
|
||||
set(HOSTAP_LDFLAGS "-L${LIBNL_INSTALL_DIR}/lib")
|
||||
list(APPEND HOSTAP_ENV
|
||||
PKG_CONFIG_PATH=${LIBNL_INSTALL_DIR}/lib/pkgconfig
|
||||
EXTRA_CFLAGS=${HOSTAP_CFLAGS}
|
||||
LDFLAGS=${HOSTAP_LDFLAGS}
|
||||
)
|
||||
endif()
|
||||
if(HOSTAP_CFLAGS)
|
||||
list(APPEND HOSTAP_ENV "EXTRA_CFLAGS=${HOSTAP_CFLAGS}")
|
||||
endif()
|
||||
find_package(Git REQUIRED)
|
||||
ExternalProject_Add(hostap
|
||||
SOURCE_DIR ${HOSTAP_DIR}
|
||||
@@ -101,11 +123,11 @@ ExternalProject_Add(hostap
|
||||
#GIT_REPOSITORY https://github.com/vanilla-wiiu/drc-hostap.git
|
||||
DOWNLOAD_COMMAND
|
||||
${CMAKE_COMMAND} -E remove_directory "${HOSTAP_DIR}" &&
|
||||
${CMAKE_COMMAND} -E env GIT_DIR=
|
||||
${GIT_EXECUTABLE} clone https://github.com/vanilla-wiiu/drc-hostap.git "${HOSTAP_DIR}" &&
|
||||
${GIT_EXECUTABLE} -C "${HOSTAP_DIR}" checkout af1da944765c6d04002584ea0d70c43899a5e6ea
|
||||
${CMAKE_COMMAND} -E env --unset=GIT_DIR ${GIT_EXECUTABLE} clone https://github.com/vanilla-wiiu/drc-hostap.git "${HOSTAP_DIR}" &&
|
||||
${CMAKE_COMMAND} -E env --unset=GIT_DIR ${GIT_EXECUTABLE} -C "${HOSTAP_DIR}" checkout a2f705daa143791180754100194c91714f7e8801
|
||||
CONFIGURE_COMMAND ${CMAKE_COMMAND} -E copy_if_different conf/wpa_supplicant.config wpa_supplicant/.config
|
||||
BUILD_COMMAND env ${HOSTAP_ENV} make -C wpa_supplicant -j${CMAKE_BUILD_PARALLEL_LEVEL} libwpa_supplicant.a libwpa_client.a
|
||||
BUILD_COMMAND env ${HOSTAP_ENV} make -C wpa_supplicant clean &&
|
||||
env ${HOSTAP_ENV} make -C wpa_supplicant -j${CMAKE_BUILD_PARALLEL_LEVEL} libwpa_supplicant.a libwpa_client.a
|
||||
INSTALL_COMMAND ""
|
||||
UPDATE_COMMAND ""
|
||||
BUILD_BYPRODUCTS
|
||||
@@ -113,7 +135,7 @@ ExternalProject_Add(hostap
|
||||
${HOSTAP_DIR}/wpa_supplicant/libwpa_client.a
|
||||
)
|
||||
if (VANILLA_BUILD_VENDORED)
|
||||
add_dependencies(hostap openssl libnl)
|
||||
add_dependencies(hostap libnl)
|
||||
endif()
|
||||
|
||||
# Define wpa_client library from hostap
|
||||
@@ -132,26 +154,25 @@ set_target_properties(wpa_supplicant_drc PROPERTIES
|
||||
)
|
||||
add_dependencies(wpa_supplicant_drc hostap)
|
||||
|
||||
# Compile custom dhclient library
|
||||
add_subdirectory(dhcp EXCLUDE_FROM_ALL)
|
||||
|
||||
# Define vanilla-pipe include directories
|
||||
target_include_directories(vanilla-pipe PRIVATE
|
||||
${HOSTAP_DIR}/src
|
||||
${HOSTAP_DIR}/src/common
|
||||
${HOSTAP_DIR}/wpa_supplicant
|
||||
${CMAKE_SOURCE_DIR}/lib
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../lib
|
||||
${LibNL_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
# Link our library with the client library
|
||||
target_link_libraries(vanilla-pipe PRIVATE
|
||||
wpa_client
|
||||
pthread
|
||||
udhcpc
|
||||
wpa_supplicant_drc
|
||||
${LibNL_LIBRARY}
|
||||
${LibNL_ROUTE_LIBRARY}
|
||||
${LibNL_GENL_LIBRARY}
|
||||
${OPENSSL_LIBRARIES}
|
||||
wpa_supplicant_drc
|
||||
${CMAKE_DL_LIBS}
|
||||
m
|
||||
)
|
||||
if(NOT ANDROID)
|
||||
target_link_libraries(vanilla-pipe PRIVATE pthread)
|
||||
endif()
|
||||
@@ -0,0 +1,472 @@
|
||||
#include "android.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <linux/fib_rules.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <limits.h>
|
||||
#include <net/if.h>
|
||||
#include <netlink/addr.h>
|
||||
#include <netlink/netlink.h>
|
||||
#include <netlink/route/nexthop.h>
|
||||
#include <netlink/route/route.h>
|
||||
#include <netlink/route/rule.h>
|
||||
#include <netlink/socket.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "wpa.h"
|
||||
|
||||
static int restore_wifi;
|
||||
static int restore_wifi_scan_always;
|
||||
|
||||
#define ANDROID_WIFI_SHUTDOWN_TIMEOUT_MS 30000
|
||||
#define ANDROID_WIFI_QUIET_PERIOD_MS 6000
|
||||
#define ANDROID_WIFI_POLL_INTERVAL_US 250000
|
||||
#define ANDROID_WIFI_STATUS_POLL_INTERVAL_MS 1000
|
||||
|
||||
#define VANILLA_CONSOLE_ADDRESS "192.168.1.10/32"
|
||||
#define VANILLA_CONSOLE_RULE_PRIORITY 9999
|
||||
|
||||
static struct rtnl_route *console_route;
|
||||
static struct rtnl_rule *console_rule;
|
||||
|
||||
typedef enum {
|
||||
ANDROID_WIFI_STATE_UNKNOWN,
|
||||
ANDROID_WIFI_STATE_DISABLED,
|
||||
ANDROID_WIFI_STATE_NOT_DISABLED,
|
||||
} android_wifi_state_t;
|
||||
|
||||
typedef enum {
|
||||
ANDROID_WIFI_STATUS_UNAVAILABLE,
|
||||
ANDROID_WIFI_STATUS_CMD,
|
||||
ANDROID_WIFI_STATUS_DUMPSYS,
|
||||
} android_wifi_status_source_t;
|
||||
|
||||
static struct rtnl_route *create_console_route(int ifindex,
|
||||
struct nl_addr *destination,
|
||||
struct nl_addr *source_address)
|
||||
{
|
||||
struct rtnl_route *route = rtnl_route_alloc();
|
||||
struct rtnl_nexthop *next_hop = rtnl_route_nh_alloc();
|
||||
if (!route || !next_hop) {
|
||||
if (route)
|
||||
rtnl_route_put(route);
|
||||
if (next_hop)
|
||||
rtnl_route_nh_free(next_hop);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rtnl_route_set_family(route, AF_INET);
|
||||
rtnl_route_set_table(route, RT_TABLE_MAIN);
|
||||
rtnl_route_set_protocol(route, RTPROT_STATIC);
|
||||
rtnl_route_set_scope(route, RT_SCOPE_LINK);
|
||||
rtnl_route_set_type(route, RTN_UNICAST);
|
||||
if (rtnl_route_set_dst(route, destination) < 0 ||
|
||||
rtnl_route_set_pref_src(route, source_address) < 0) {
|
||||
rtnl_route_put(route);
|
||||
rtnl_route_nh_free(next_hop);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rtnl_route_nh_set_ifindex(next_hop, ifindex);
|
||||
rtnl_route_add_nexthop(route, next_hop);
|
||||
return route;
|
||||
}
|
||||
|
||||
static struct rtnl_rule *create_console_rule(struct nl_addr *destination)
|
||||
{
|
||||
struct rtnl_rule *rule = rtnl_rule_alloc();
|
||||
if (rule) {
|
||||
rtnl_rule_set_family(rule, AF_INET);
|
||||
rtnl_rule_set_prio(rule, VANILLA_CONSOLE_RULE_PRIORITY);
|
||||
rtnl_rule_set_table(rule, RT_TABLE_MAIN);
|
||||
rtnl_rule_set_action(rule, FR_ACT_TO_TBL);
|
||||
rtnl_rule_set_dst(rule, destination);
|
||||
}
|
||||
return rule;
|
||||
}
|
||||
|
||||
int vanilla_android_install_console_routing(struct nl_sock *socket,
|
||||
int ifindex,
|
||||
struct nl_addr *source_address)
|
||||
{
|
||||
// Remove any previously made routing (during this session)
|
||||
vanilla_android_remove_console_routing(socket);
|
||||
|
||||
// Sanity check input
|
||||
struct nl_addr *destination = NULL;
|
||||
if (!socket || ifindex <= 0 || !source_address || nl_addr_parse(VANILLA_CONSOLE_ADDRESS, AF_INET, &destination) < 0) {
|
||||
nlprint("ANDROID: INVALID INPUT");
|
||||
goto die;
|
||||
}
|
||||
|
||||
// Build route and rule
|
||||
struct rtnl_route *route = create_console_route(ifindex, destination, source_address);
|
||||
struct rtnl_rule *rule = create_console_rule(destination);
|
||||
nl_addr_put(destination);
|
||||
if (!route || !rule) {
|
||||
nlprint("ANDROID: FAILED TO BUILD WII U ROUTING");
|
||||
if (route) {
|
||||
rtnl_route_put(route);
|
||||
}
|
||||
if (rule) {
|
||||
rtnl_rule_put(rule);
|
||||
}
|
||||
goto die;
|
||||
}
|
||||
|
||||
// Remove an existing rule if a previous pipe was killed without cleaning up
|
||||
rtnl_rule_delete(socket, rule, 0);
|
||||
|
||||
// Add route
|
||||
int result = rtnl_route_add(socket, route, NLM_F_CREATE | NLM_F_REPLACE);
|
||||
if (result < 0) {
|
||||
nlprint("ANDROID: FAILED TO ADD WII U ROUTE: %s", nl_geterror(result));
|
||||
goto die_and_put;
|
||||
}
|
||||
|
||||
// Add rule
|
||||
result = rtnl_rule_add(socket, rule, NLM_F_CREATE | NLM_F_EXCL);
|
||||
if (result < 0) {
|
||||
nlprint("ANDROID: FAILED TO ADD WII U ROUTING RULE: %s", nl_geterror(result));
|
||||
goto die_and_delete_route;
|
||||
}
|
||||
|
||||
// Everything worked, store route/rule so we can remove later
|
||||
console_route = route;
|
||||
console_rule = rule;
|
||||
nlprint("ANDROID: NETWORK ROUTE AND RULE ADDED");
|
||||
return 0;
|
||||
|
||||
die_and_delete_route:
|
||||
rtnl_route_delete(socket, route, 0);
|
||||
|
||||
die_and_put:
|
||||
rtnl_route_put(route);
|
||||
rtnl_rule_put(rule);
|
||||
|
||||
die:
|
||||
return -1;
|
||||
}
|
||||
|
||||
void vanilla_android_remove_console_routing(struct nl_sock *socket)
|
||||
{
|
||||
if (!socket) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (console_rule) {
|
||||
rtnl_rule_delete(socket, console_rule, 0);
|
||||
rtnl_rule_put(console_rule);
|
||||
console_rule = NULL;
|
||||
}
|
||||
|
||||
if (console_route) {
|
||||
rtnl_route_delete(socket, console_route, 0);
|
||||
rtnl_route_put(console_route);
|
||||
console_route = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static android_wifi_state_t parse_wifi_state(const char *status)
|
||||
{
|
||||
if (strstr(status, "Wifi is disabled") ||
|
||||
strstr(status, "Wi-Fi is disabled"))
|
||||
return ANDROID_WIFI_STATE_DISABLED;
|
||||
|
||||
if (strstr(status, "Wifi is enabled") ||
|
||||
strstr(status, "Wi-Fi is enabled") ||
|
||||
strstr(status, "Wifi is enabling") ||
|
||||
strstr(status, "Wi-Fi is enabling") ||
|
||||
strstr(status, "Wifi is disabling") ||
|
||||
strstr(status, "Wi-Fi is disabling"))
|
||||
return ANDROID_WIFI_STATE_NOT_DISABLED;
|
||||
|
||||
return ANDROID_WIFI_STATE_UNKNOWN;
|
||||
}
|
||||
|
||||
static android_wifi_state_t query_wifi_state(android_wifi_status_source_t source)
|
||||
{
|
||||
const char **command;
|
||||
const char *cmd_status[] = {"cmd", "wifi", "status", NULL};
|
||||
const char *dumpsys_status[] = {"dumpsys", "wifi", NULL};
|
||||
|
||||
if (source == ANDROID_WIFI_STATUS_CMD) {
|
||||
command = cmd_status;
|
||||
} else if (source == ANDROID_WIFI_STATUS_DUMPSYS) {
|
||||
command = dumpsys_status;
|
||||
} else {
|
||||
return ANDROID_WIFI_STATE_UNKNOWN;
|
||||
}
|
||||
|
||||
char status[256];
|
||||
ssize_t status_len = run_process_and_read_stdout(
|
||||
command, status, sizeof(status) - 1);
|
||||
if (status_len <= 0)
|
||||
return ANDROID_WIFI_STATE_UNKNOWN;
|
||||
|
||||
status[status_len] = 0;
|
||||
return parse_wifi_state(status);
|
||||
}
|
||||
|
||||
static android_wifi_status_source_t find_wifi_status_source(android_wifi_state_t *state)
|
||||
{
|
||||
*state = query_wifi_state(ANDROID_WIFI_STATUS_CMD);
|
||||
if (*state != ANDROID_WIFI_STATE_UNKNOWN) {
|
||||
return ANDROID_WIFI_STATUS_CMD;
|
||||
}
|
||||
|
||||
*state = query_wifi_state(ANDROID_WIFI_STATUS_DUMPSYS);
|
||||
if (*state != ANDROID_WIFI_STATE_UNKNOWN) {
|
||||
return ANDROID_WIFI_STATUS_DUMPSYS;
|
||||
}
|
||||
|
||||
return ANDROID_WIFI_STATUS_UNAVAILABLE;
|
||||
}
|
||||
|
||||
static int query_wifi_enabled_setting(int *enabled)
|
||||
{
|
||||
char status[64];
|
||||
ssize_t status_len = run_process_and_read_stdout((const char *[]) {"settings", "get", "global", "wifi_on", NULL}, status, sizeof(status) - 1);
|
||||
if (status_len <= 0)
|
||||
return -1;
|
||||
|
||||
status[status_len] = 0;
|
||||
char *end;
|
||||
errno = 0;
|
||||
long value = strtol(status, &end, 10);
|
||||
while (*end && isspace((unsigned char) *end)) {
|
||||
end++;
|
||||
}
|
||||
|
||||
if (errno || end == status || *end) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// WifiSettingsStore uses 1 for enabled and 3 for enabled while overriding
|
||||
// airplane mode (0 and 2 mean disabled)
|
||||
*enabled = value == 1 || value == 3;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int query_wifi_scan_always_setting(int *enabled)
|
||||
{
|
||||
char status[64];
|
||||
ssize_t status_len = run_process_and_read_stdout((const char *[]) {"settings", "get", "global", "wifi_scan_always_enabled", NULL}, status, sizeof(status) - 1);
|
||||
if (status_len <= 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
status[status_len] = 0;
|
||||
char *end;
|
||||
errno = 0;
|
||||
long value = strtol(status, &end, 10);
|
||||
while (*end && isspace((unsigned char) *end)) {
|
||||
end++;
|
||||
}
|
||||
if (errno || end == status || *end) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
*enabled = value != 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_wifi_scan_always_setting(int enabled)
|
||||
{
|
||||
return run_process_and_read_stdout((const char *[]) {"settings", "put", "global", "wifi_scan_always_enabled", enabled ? "1" : "0", NULL}, NULL, 0) < 0 ? -1 : 0;
|
||||
}
|
||||
|
||||
static void restore_android_wifi_state(void)
|
||||
{
|
||||
if (restore_wifi) {
|
||||
nlprint("ANDROID: RESTORING THE SYSTEM WI-FI");
|
||||
run_process_and_read_stdout((const char *[]) {"svc", "wifi", "enable", NULL}, NULL, 0);
|
||||
restore_wifi = 0;
|
||||
}
|
||||
|
||||
if (restore_wifi_scan_always) {
|
||||
nlprint("ANDROID: RESTORING BACKGROUND WI-FI SCANNING");
|
||||
set_wifi_scan_always_setting(1);
|
||||
restore_wifi_scan_always = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int process_named_is_running(const char *name)
|
||||
{
|
||||
DIR *proc = opendir("/proc");
|
||||
if (!proc) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int found = 0;
|
||||
struct dirent *entry;
|
||||
while (!found && (entry = readdir(proc))) {
|
||||
if (!isdigit((unsigned char) entry->d_name[0])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
char comm_path[PATH_MAX];
|
||||
if (snprintf(comm_path, sizeof(comm_path), "/proc/%s/comm", entry->d_name) >= (int) sizeof(comm_path)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
FILE *comm = fopen(comm_path, "r");
|
||||
if (!comm) {
|
||||
continue;
|
||||
}
|
||||
|
||||
char process_name[64];
|
||||
if (fgets(process_name, sizeof(process_name), comm)) {
|
||||
process_name[strcspn(process_name, "\r\n")] = 0;
|
||||
found = !strcmp(process_name, name);
|
||||
}
|
||||
fclose(comm);
|
||||
}
|
||||
|
||||
closedir(proc);
|
||||
return found;
|
||||
}
|
||||
|
||||
static int stock_supplicant_is_running(void)
|
||||
{
|
||||
return process_named_is_running("wpa_supplicant") || process_named_is_running("p2p_supplicant");
|
||||
}
|
||||
|
||||
static int64_t monotonic_milliseconds(void)
|
||||
{
|
||||
struct timespec now;
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &now) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (int64_t) now.tv_sec * 1000 + now.tv_nsec / 1000000;
|
||||
}
|
||||
|
||||
static int wait_for_wifi_shutdown(android_wifi_status_source_t status_source)
|
||||
{
|
||||
int64_t started_at = monotonic_milliseconds();
|
||||
int64_t quiet_since = -1;
|
||||
int64_t next_status_check = started_at;
|
||||
android_wifi_state_t last_state = ANDROID_WIFI_STATE_UNKNOWN;
|
||||
int supplicant_running = 0;
|
||||
|
||||
if (started_at < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
nlprint("ANDROID: WAITING FOR THE SYSTEM WI-FI TO RELEASE WLAN");
|
||||
while (1) {
|
||||
int64_t now = monotonic_milliseconds();
|
||||
if (now < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (status_source != ANDROID_WIFI_STATUS_UNAVAILABLE
|
||||
&& now >= next_status_check) {
|
||||
last_state = query_wifi_state(status_source);
|
||||
now = monotonic_milliseconds();
|
||||
if (now < 0) {
|
||||
return -1;
|
||||
}
|
||||
next_status_check = now + ANDROID_WIFI_STATUS_POLL_INTERVAL_MS;
|
||||
}
|
||||
|
||||
// Some Android builds may keep an idle supplicant service alive even
|
||||
// after disabling Wi-Fi. Check for it.
|
||||
supplicant_running = status_source == ANDROID_WIFI_STATUS_UNAVAILABLE && stock_supplicant_is_running();
|
||||
|
||||
// Some Android builds may attempt to recover the Wi-Fi, wait to ensure
|
||||
// that doesn't happen
|
||||
int framework_disabled = status_source == ANDROID_WIFI_STATUS_UNAVAILABLE || last_state == ANDROID_WIFI_STATE_DISABLED;
|
||||
if (framework_disabled && !supplicant_running) {
|
||||
if (quiet_since < 0) {
|
||||
quiet_since = now;
|
||||
}
|
||||
if (now - quiet_since >= ANDROID_WIFI_QUIET_PERIOD_MS) {
|
||||
nlprint("ANDROID: SYSTEM WI-FI RELEASED WLAN");
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
quiet_since = -1;
|
||||
}
|
||||
|
||||
if (now - started_at >= ANDROID_WIFI_SHUTDOWN_TIMEOUT_MS) {
|
||||
break;
|
||||
}
|
||||
|
||||
usleep(ANDROID_WIFI_POLL_INTERVAL_US);
|
||||
}
|
||||
|
||||
if (status_source == ANDROID_WIFI_STATUS_UNAVAILABLE) {
|
||||
nlprint("ANDROID: TIMED OUT WAITING FOR THE SYSTEM WI-FI "
|
||||
"(supplicant=%s)",
|
||||
supplicant_running ? "running" : "stopped");
|
||||
} else {
|
||||
nlprint("ANDROID: TIMED OUT WAITING FOR THE SYSTEM WI-FI "
|
||||
"(state=%s)",
|
||||
last_state == ANDROID_WIFI_STATE_DISABLED ? "disabled" :
|
||||
last_state == ANDROID_WIFI_STATE_NOT_DISABLED ? "active" :
|
||||
"unknown");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int vanilla_android_acquire_wifi(const char *wireless_interface)
|
||||
{
|
||||
android_wifi_state_t initial_state;
|
||||
android_wifi_status_source_t status_source = find_wifi_status_source(&initial_state);
|
||||
if (query_wifi_enabled_setting(&restore_wifi) < 0) {
|
||||
restore_wifi = initial_state != ANDROID_WIFI_STATE_DISABLED;
|
||||
}
|
||||
|
||||
// Android may continue doing location scans even while Wi-Fi is off,
|
||||
// conflicting with us. Ensure background scanning is also off.
|
||||
restore_wifi_scan_always = 0;
|
||||
int scan_always_enabled;
|
||||
if (query_wifi_scan_always_setting(&scan_always_enabled) == 0 &&
|
||||
scan_always_enabled) {
|
||||
nlprint("ANDROID: TEMPORARILY DISABLING BACKGROUND WI-FI SCANNING");
|
||||
if (set_wifi_scan_always_setting(0) < 0) {
|
||||
nlprint("ANDROID: FAILED TO DISABLE BACKGROUND WI-FI SCANNING");
|
||||
return -1;
|
||||
}
|
||||
restore_wifi_scan_always = 1;
|
||||
}
|
||||
|
||||
nlprint("ANDROID: TEMPORARILY DISABLING THE SYSTEM WI-FI");
|
||||
if (run_process_and_read_stdout(
|
||||
(const char *[]) {"svc", "wifi", "disable", NULL}, NULL, 0) < 0) {
|
||||
nlprint("ANDROID: FAILED TO DISABLE THE SYSTEM WI-FI");
|
||||
restore_android_wifi_state();
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (wait_for_wifi_shutdown(status_source) < 0) {
|
||||
restore_android_wifi_state();
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!if_nametoindex(wireless_interface)) {
|
||||
nlprint("ANDROID: INTERFACE %s DISAPPEARED WHEN WI-FI WAS DISABLED; "
|
||||
"THIS DEVICE NEEDS A VENDOR-SPECIFIC RADIO BACKEND",
|
||||
wireless_interface);
|
||||
restore_android_wifi_state();
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void vanilla_android_release_wifi(void)
|
||||
{
|
||||
restore_android_wifi_state();
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifndef VANILLA_PIPE_ANDROID_H
|
||||
#define VANILLA_PIPE_ANDROID_H
|
||||
|
||||
struct nl_sock;
|
||||
struct nl_addr;
|
||||
|
||||
int vanilla_android_acquire_wifi(const char *wireless_interface);
|
||||
void vanilla_android_release_wifi(void);
|
||||
|
||||
int vanilla_android_install_console_routing(struct nl_sock *socket,
|
||||
int ifindex,
|
||||
struct nl_addr *source_address);
|
||||
void vanilla_android_remove_console_routing(struct nl_sock *socket);
|
||||
|
||||
#endif // VANILLA_PIPE_ANDROID_H
|
||||
@@ -1,13 +0,0 @@
|
||||
udhcp server/client package
|
||||
-----------------------
|
||||
|
||||
Russ Dill <Russ.Dill@asu.edu>
|
||||
Matthew Ramsay <matthewr@moreton.com.au>
|
||||
Chris Trew <christ@moreton.com.au>
|
||||
|
||||
Other Credits:
|
||||
--------------
|
||||
Moreton Bay (http://www.moretonbay.com/)
|
||||
Vladimir Oleynik <dzo@simtrea.ru> Size optimizations
|
||||
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
add_library(udhcpc STATIC
|
||||
common.c
|
||||
options.c
|
||||
packet.c
|
||||
pidfile.c
|
||||
signalpipe.c
|
||||
socket.c
|
||||
dhcpc.c
|
||||
clientpacket.c
|
||||
clientsocket.c
|
||||
script.c
|
||||
)
|
||||
|
||||
target_compile_definitions(udhcpc PRIVATE _GNU_SOURCE $<$<CONFIG:Debug>:UDHCP_DEBUG>)
|
||||
@@ -1,339 +0,0 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
675 Mass Ave, Cambridge, MA 02139, USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Appendix: How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) 19yy <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) 19yy name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
Public License instead of this License.
|
||||
@@ -1,142 +0,0 @@
|
||||
udhcp client (udhcpc)
|
||||
--------------------
|
||||
|
||||
The udhcp client negotiates a lease with the DHCP server and notifies
|
||||
a set of scripts when a leases is obtained or lost.
|
||||
|
||||
|
||||
command line options
|
||||
-------------------
|
||||
|
||||
The command line options for the udhcp client are:
|
||||
|
||||
-c, --clientid=CLIENTID Client identifier
|
||||
-H, --hostname=HOSTNAME Client hostname
|
||||
-h, Alias for -H
|
||||
-F, --fqdn=FQDN Client fully qualified domain name
|
||||
-f, --foreground Do not fork after getting lease
|
||||
-b, --background Fork to background if lease cannot be
|
||||
immediately negotiated.
|
||||
-i, --interface=INTERFACE Interface to use (default: eth0)
|
||||
-n, --now Exit with failure if lease cannot be
|
||||
immediately negotiated.
|
||||
-p, --pidfile=file Store process ID of daemon in file
|
||||
-q, --quit Quit after obtaining lease
|
||||
-r, --request=IP IP address to request (default: none)
|
||||
-s, --script=file Run file at dhcp events (default:
|
||||
/etc/udhcpc/default.script)
|
||||
-v, --version Display version
|
||||
|
||||
|
||||
If the requested IP address cannot be obtained, the client accepts the
|
||||
address that the server offers.
|
||||
|
||||
|
||||
udhcp client scripts
|
||||
-------------------
|
||||
|
||||
When an event occurs, udhcpc calls the action script. udhcpc never does
|
||||
any configuration of the network interface itself, but instead relies on
|
||||
a set of scripts. The script by default is
|
||||
/etc/udhcpc/default.script but this can be changed via the command
|
||||
line arguments. The three possible arguments to the script are:
|
||||
|
||||
deconfig: This argument is used when udhcpc starts, and
|
||||
when a leases is lost. The script must put the interface in an
|
||||
up, but deconfigured state, ie: ifconfig $interface 0.0.0.0.
|
||||
|
||||
bound: This argument is used when udhcpc moves from an
|
||||
unbound, to a bound state. All of the paramaters are set in
|
||||
enviromental variables, The script should configure the interface,
|
||||
and set any other relavent parameters (default gateway, dns server,
|
||||
etc).
|
||||
|
||||
renew: This argument is used when a DHCP lease is renewed. All of
|
||||
the paramaters are set in enviromental variables. This argument is
|
||||
used when the interface is already configured, so the IP address,
|
||||
will not change, however, the other DHCP paramaters, such as the
|
||||
default gateway, subnet mask, and dns server may change.
|
||||
|
||||
nak: This argument is used with udhcpc receives a NAK message.
|
||||
The script with the deconfig argument will be called directly
|
||||
afterwards, so no changes to the network interface are neccessary.
|
||||
This hook is provided for purely informational purposes (the
|
||||
message option may contain a reason for the NAK).
|
||||
|
||||
The paramaters for enviromental variables are as follows:
|
||||
|
||||
$HOME - The set $HOME env or "/"
|
||||
$PATH - the set $PATH env or "/bin:/usr/bin:/sbin:/usr/sbin"
|
||||
$1 - What action the script should perform
|
||||
interface - The interface this was obtained on
|
||||
ip - The obtained IP
|
||||
mask - The number of bits in the netmask (ie: 24)
|
||||
siaddr - The bootp next server option
|
||||
sname - The bootp server name option
|
||||
boot_file - The bootp boot file option
|
||||
subnet - The assigend subnet mask
|
||||
timezone - Offset in seconds from UTC
|
||||
router - A list of routers
|
||||
timesvr - A list of time servers
|
||||
namesvr - A list of IEN 116 name servers
|
||||
dns - A list of DNS server
|
||||
logsvr - A list of MIT-LCS UDP log servers
|
||||
cookiesvr - A list of RFC 865 cookie servers
|
||||
lprsvr - A list of LPR servers
|
||||
hostname - The assigned hostname
|
||||
bootsize - The length in 512 octect blocks of the bootfile
|
||||
domain - The domain name of the network
|
||||
swapsvr - The IP address of the client's swap server
|
||||
rootpath - The path name of the client's root disk
|
||||
ipttl - The TTL to use for this network
|
||||
mtu - The MTU to use for this network
|
||||
broadcast - The broadcast address for this network
|
||||
ntpsrv - A list of NTP servers
|
||||
wins - A list of WINS servers
|
||||
lease - The lease time, in seconds
|
||||
dhcptype - DHCP message type (safely ignored)
|
||||
serverid - The IP of the server
|
||||
message - Reason for a DHCPNAK
|
||||
tftp - The TFTP server name
|
||||
bootfile - The bootfile name
|
||||
|
||||
additional options are easily added in options.c.
|
||||
|
||||
|
||||
note on udhcpc's random seed
|
||||
---------------------------
|
||||
|
||||
udhcpc will seed its random number generator (used for generating xid's)
|
||||
by reading /dev/urandom. If you have a lot of embedded systems on the same
|
||||
network, with no entropy, you can either seed /dev/urandom by a method of
|
||||
your own, or doing the following on startup:
|
||||
|
||||
ifconfig eth0 > /dev/urandom
|
||||
|
||||
in order to seed /dev/urandom with some data (mac address) unique to your
|
||||
system. If reading /dev/urandom fails, udhcpc will fall back to its old
|
||||
behavior of seeding with time(0).
|
||||
|
||||
|
||||
signals accepted by udhcpc
|
||||
-------------------------
|
||||
|
||||
udhcpc also responds to SIGUSR1 and SIGUSR2. SIGUSR1 will force a renew state,
|
||||
and SIGUSR2 will force a release of the current lease, and cause udhcpc to
|
||||
go into an inactive state (until it is killed, or receives a SIGUSR1). You do
|
||||
not need to sleep between sending signals, as signals received are processed
|
||||
sequencially in the order they are received.
|
||||
|
||||
|
||||
compile time options
|
||||
-------------------
|
||||
|
||||
options.c contains a set of dhcp options for the client:
|
||||
|
||||
name[10]: The name of the option as it will appear in scripts
|
||||
|
||||
flags: The type of option, as well as if it will be requested
|
||||
by the client (OPTION_REQ)
|
||||
|
||||
code: The DHCP code for this option
|
||||
|
||||
@@ -1,249 +0,0 @@
|
||||
/* clientpacket.c
|
||||
*
|
||||
* Packet generation and dispatching functions for the DHCP client.
|
||||
*
|
||||
* Russ Dill <Russ.Dill@asu.edu> July 2001
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <features.h>
|
||||
#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1
|
||||
#include <netpacket/packet.h>
|
||||
#include <net/ethernet.h>
|
||||
#else
|
||||
#include <asm/types.h>
|
||||
#include <linux/if_packet.h>
|
||||
#include <linux/if_ether.h>
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
|
||||
#include "dhcpd.h"
|
||||
#include "clientpacket.h"
|
||||
#include "options.h"
|
||||
#include "dhcpc.h"
|
||||
#include "common.h"
|
||||
|
||||
|
||||
/* Create a random xid */
|
||||
unsigned long random_xid(void)
|
||||
{
|
||||
static int initialized;
|
||||
if (!initialized) {
|
||||
int fd;
|
||||
unsigned long seed;
|
||||
|
||||
fd = open("/dev/urandom", 0);
|
||||
if (fd < 0 || read(fd, &seed, sizeof(seed)) < 0) {
|
||||
LOG(LOG_WARNING, "Could not load seed from /dev/urandom: %m");
|
||||
seed = time(0);
|
||||
}
|
||||
if (fd >= 0) close(fd);
|
||||
srand(seed);
|
||||
initialized++;
|
||||
}
|
||||
return rand();
|
||||
}
|
||||
|
||||
|
||||
/* initialize a packet with the proper defaults */
|
||||
static void init_packet(struct dhcpMessage *packet, char type)
|
||||
{
|
||||
struct vendor {
|
||||
char vendor, length;
|
||||
char str[sizeof("udhcp "VERSION)];
|
||||
} vendor_id = { DHCP_VENDOR, sizeof("udhcp "VERSION) - 1, "udhcp "VERSION};
|
||||
|
||||
init_header(packet, type);
|
||||
memcpy(packet->chaddr, client_config.arp, 6);
|
||||
add_option_string(packet->options, client_config.clientid);
|
||||
if (client_config.hostname) add_option_string(packet->options, client_config.hostname);
|
||||
if (client_config.fqdn) add_option_string(packet->options, client_config.fqdn);
|
||||
add_option_string(packet->options, (uint8_t *) &vendor_id);
|
||||
}
|
||||
|
||||
|
||||
/* Add a parameter request list for stubborn DHCP servers. Pull the data
|
||||
* from the struct in options.c. Don't do bounds checking here because it
|
||||
* goes towards the head of the packet. */
|
||||
static void add_requests(struct dhcpMessage *packet)
|
||||
{
|
||||
int end = end_option(packet->options);
|
||||
int i, len = 0;
|
||||
|
||||
packet->options[end + OPT_CODE] = DHCP_PARAM_REQ;
|
||||
for (i = 0; dhcp_options[i].code; i++)
|
||||
if (dhcp_options[i].flags & OPTION_REQ)
|
||||
packet->options[end + OPT_DATA + len++] = dhcp_options[i].code;
|
||||
packet->options[end + OPT_LEN] = len;
|
||||
packet->options[end + OPT_DATA + len] = DHCP_END;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Broadcast a DHCP discover packet to the network, with an optionally requested IP */
|
||||
int send_discover(unsigned long xid, unsigned long requested)
|
||||
{
|
||||
struct dhcpMessage packet;
|
||||
|
||||
init_packet(&packet, DHCPDISCOVER);
|
||||
packet.xid = xid;
|
||||
if (requested)
|
||||
add_simple_option(packet.options, DHCP_REQUESTED_IP, requested);
|
||||
|
||||
add_requests(&packet);
|
||||
LOG(LOG_DEBUG, "Sending discover...");
|
||||
return raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST,
|
||||
SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex);
|
||||
}
|
||||
|
||||
|
||||
/* Broadcasts a DHCP request message */
|
||||
int send_selecting(unsigned long xid, unsigned long server, unsigned long requested)
|
||||
{
|
||||
struct dhcpMessage packet;
|
||||
struct in_addr addr;
|
||||
|
||||
init_packet(&packet, DHCPREQUEST);
|
||||
packet.xid = xid;
|
||||
|
||||
add_simple_option(packet.options, DHCP_REQUESTED_IP, requested);
|
||||
add_simple_option(packet.options, DHCP_SERVER_ID, server);
|
||||
|
||||
add_requests(&packet);
|
||||
addr.s_addr = requested;
|
||||
LOG(LOG_DEBUG, "Sending select for %s...", inet_ntoa(addr));
|
||||
return raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST,
|
||||
SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex);
|
||||
}
|
||||
|
||||
|
||||
/* Unicasts or broadcasts a DHCP renew message */
|
||||
int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr)
|
||||
{
|
||||
struct dhcpMessage packet;
|
||||
int ret = 0;
|
||||
|
||||
init_packet(&packet, DHCPREQUEST);
|
||||
packet.xid = xid;
|
||||
packet.ciaddr = ciaddr;
|
||||
|
||||
add_requests(&packet);
|
||||
LOG(LOG_DEBUG, "Sending renew...");
|
||||
if (server)
|
||||
ret = kernel_packet(&packet, ciaddr, CLIENT_PORT, server, SERVER_PORT);
|
||||
else ret = raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST,
|
||||
SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* Unicasts a DHCP release message */
|
||||
int send_release(unsigned long server, unsigned long ciaddr)
|
||||
{
|
||||
struct dhcpMessage packet;
|
||||
|
||||
init_packet(&packet, DHCPRELEASE);
|
||||
packet.xid = random_xid();
|
||||
packet.ciaddr = ciaddr;
|
||||
|
||||
add_simple_option(packet.options, DHCP_REQUESTED_IP, ciaddr);
|
||||
add_simple_option(packet.options, DHCP_SERVER_ID, server);
|
||||
|
||||
LOG(LOG_DEBUG, "Sending release...");
|
||||
return kernel_packet(&packet, ciaddr, CLIENT_PORT, server, SERVER_PORT);
|
||||
}
|
||||
|
||||
|
||||
/* return -1 on errors that are fatal for the socket, -2 for those that aren't */
|
||||
int get_raw_packet(struct dhcpMessage *payload, int fd)
|
||||
{
|
||||
int bytes;
|
||||
struct udp_dhcp_packet packet;
|
||||
uint32_t source, dest;
|
||||
uint16_t check;
|
||||
|
||||
memset(&packet, 0, sizeof(struct udp_dhcp_packet));
|
||||
bytes = read(fd, &packet, sizeof(struct udp_dhcp_packet));
|
||||
if (bytes < 0) {
|
||||
DEBUG(LOG_INFO, "couldn't read on raw listening socket -- ignoring");
|
||||
usleep(500000); /* possible down interface, looping condition */
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (bytes < (int) (sizeof(struct iphdr) + sizeof(struct udphdr))) {
|
||||
DEBUG(LOG_INFO, "message too short, ignoring");
|
||||
return -2;
|
||||
}
|
||||
|
||||
if (bytes < ntohs(packet.ip.tot_len)) {
|
||||
DEBUG(LOG_INFO, "Truncated packet");
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* ignore any extra garbage bytes */
|
||||
bytes = ntohs(packet.ip.tot_len);
|
||||
|
||||
/* Make sure its the right packet for us, and that it passes sanity checks */
|
||||
if (packet.ip.protocol != IPPROTO_UDP || packet.ip.version != IPVERSION ||
|
||||
packet.ip.ihl != sizeof(packet.ip) >> 2 || packet.udp.dest != htons(CLIENT_PORT) ||
|
||||
bytes > (int) sizeof(struct udp_dhcp_packet) ||
|
||||
ntohs(packet.udp.len) != (uint16_t) (bytes - sizeof(packet.ip))) {
|
||||
DEBUG(LOG_INFO, "unrelated/bogus packet");
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* check IP checksum */
|
||||
check = packet.ip.check;
|
||||
packet.ip.check = 0;
|
||||
if (check != checksum(&(packet.ip), sizeof(packet.ip))) {
|
||||
DEBUG(LOG_INFO, "bad IP header checksum, ignoring");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* verify the UDP checksum by replacing the header with a psuedo header */
|
||||
source = packet.ip.saddr;
|
||||
dest = packet.ip.daddr;
|
||||
check = packet.udp.check;
|
||||
packet.udp.check = 0;
|
||||
memset(&packet.ip, 0, sizeof(packet.ip));
|
||||
|
||||
packet.ip.protocol = IPPROTO_UDP;
|
||||
packet.ip.saddr = source;
|
||||
packet.ip.daddr = dest;
|
||||
packet.ip.tot_len = packet.udp.len; /* cheat on the psuedo-header */
|
||||
if (check && check != checksum(&packet, bytes)) {
|
||||
DEBUG(LOG_ERR, "packet with bad UDP checksum received, ignoring");
|
||||
return -2;
|
||||
}
|
||||
|
||||
memcpy(payload, &(packet.data), bytes - (sizeof(packet.ip) + sizeof(packet.udp)));
|
||||
|
||||
if (ntohl(payload->cookie) != DHCP_MAGIC) {
|
||||
LOG(LOG_ERR, "received bogus message (bad magic) -- ignoring");
|
||||
return -2;
|
||||
}
|
||||
DEBUG(LOG_INFO, "oooooh!!! got some!");
|
||||
return bytes - (sizeof(packet.ip) + sizeof(packet.udp));
|
||||
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
#ifndef _CLIENTPACKET_H
|
||||
#define _CLIENTPACKET_H
|
||||
|
||||
#include "packet.h"
|
||||
|
||||
unsigned long random_xid(void);
|
||||
int send_discover(unsigned long xid, unsigned long requested);
|
||||
int send_selecting(unsigned long xid, unsigned long server, unsigned long requested);
|
||||
int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr);
|
||||
int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr);
|
||||
int send_release(unsigned long server, unsigned long ciaddr);
|
||||
int get_raw_packet(struct dhcpMessage *payload, int fd);
|
||||
|
||||
#endif
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* clientsocket.c -- DHCP client socket creation
|
||||
*
|
||||
* udhcp client
|
||||
*
|
||||
* Russ Dill <Russ.Dill@asu.edu> July 2001
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#include <netinet/in.h>
|
||||
#include <features.h>
|
||||
#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1
|
||||
#include <netpacket/packet.h>
|
||||
#include <net/ethernet.h>
|
||||
#else
|
||||
#include <asm/types.h>
|
||||
#include <linux/if_packet.h>
|
||||
#include <linux/if_ether.h>
|
||||
#endif
|
||||
|
||||
#include "clientsocket.h"
|
||||
#include "common.h"
|
||||
|
||||
|
||||
int raw_socket(int ifindex)
|
||||
{
|
||||
int fd;
|
||||
struct sockaddr_ll sock;
|
||||
|
||||
DEBUG(LOG_INFO, "Opening raw socket on ifindex %d", ifindex);
|
||||
if ((fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) {
|
||||
DEBUG(LOG_ERR, "socket call failed: %m");
|
||||
return -1;
|
||||
}
|
||||
|
||||
sock.sll_family = AF_PACKET;
|
||||
sock.sll_protocol = htons(ETH_P_IP);
|
||||
sock.sll_ifindex = ifindex;
|
||||
if (bind(fd, (struct sockaddr *) &sock, sizeof(sock)) < 0) {
|
||||
DEBUG(LOG_ERR, "bind call failed: %m");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
/* clientsocket.h */
|
||||
#ifndef _CLIENTSOCKET_H
|
||||
#define _CLIENTSOCKET_H
|
||||
|
||||
int raw_socket(int ifindex);
|
||||
|
||||
#endif
|
||||
@@ -1,162 +0,0 @@
|
||||
/* common.c
|
||||
*
|
||||
* Functions for debugging and logging as well as some other
|
||||
* simple helper functions.
|
||||
*
|
||||
* Russ Dill <Russ.Dill@asu.edu> 2001-2003
|
||||
* Rewritten by Vladimir Oleynik <dzo@simtreas.ru> (C) 2003
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <paths.h>
|
||||
#include <sys/socket.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "pidfile.h"
|
||||
|
||||
|
||||
static int daemonized;
|
||||
|
||||
long uptime(void)
|
||||
{
|
||||
struct sysinfo info;
|
||||
sysinfo(&info);
|
||||
return info.uptime;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This function makes sure our first socket calls
|
||||
* aren't going to fd 1 (printf badness...) and are
|
||||
* not later closed by daemon()
|
||||
*/
|
||||
static inline void sanitize_fds(void)
|
||||
{
|
||||
int zero;
|
||||
if ((zero = open(_PATH_DEVNULL, O_RDWR, 0)) < 0) return;
|
||||
while (zero < 3) zero = dup(zero);
|
||||
close(zero);
|
||||
}
|
||||
|
||||
|
||||
void background(const char *pidfile)
|
||||
{
|
||||
#ifdef __uClinux__
|
||||
LOG(LOG_ERR, "Cannot background in uclinux (yet)");
|
||||
#else /* __uClinux__ */
|
||||
int pid_fd;
|
||||
|
||||
/* hold lock during fork. */
|
||||
pid_fd = pidfile_acquire(pidfile);
|
||||
if (daemon(0, 0) == -1) {
|
||||
perror("fork");
|
||||
exit(1);
|
||||
}
|
||||
daemonized++;
|
||||
pidfile_write_release(pid_fd);
|
||||
#endif /* __uClinux__ */
|
||||
}
|
||||
|
||||
|
||||
#ifdef UDHCP_SYSLOG
|
||||
void udhcp_logging(int level, const char *fmt, ...)
|
||||
{
|
||||
va_list p;
|
||||
va_list p2;
|
||||
|
||||
va_start(p, fmt);
|
||||
__va_copy(p2, p);
|
||||
if(!daemonized) {
|
||||
vprintf(fmt, p);
|
||||
putchar('\n');
|
||||
}
|
||||
vsyslog(level, fmt, p2);
|
||||
va_end(p);
|
||||
}
|
||||
|
||||
|
||||
void start_log_and_pid(const char *client_server, const char *pidfile)
|
||||
{
|
||||
int pid_fd;
|
||||
|
||||
/* Make sure our syslog fd isn't overwritten */
|
||||
sanitize_fds();
|
||||
|
||||
/* do some other misc startup stuff while we are here to save bytes */
|
||||
pid_fd = pidfile_acquire(pidfile);
|
||||
pidfile_write_release(pid_fd);
|
||||
|
||||
/* equivelent of doing a fflush after every \n */
|
||||
setlinebuf(stdout);
|
||||
|
||||
openlog(client_server, LOG_PID | LOG_CONS, LOG_LOCAL0);
|
||||
udhcp_logging(LOG_INFO, "%s (v%s) started", client_server, VERSION);
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
|
||||
|
||||
static char *syslog_level_msg[] = {
|
||||
[LOG_EMERG] = "EMERGENCY!",
|
||||
[LOG_ALERT] = "ALERT!",
|
||||
[LOG_CRIT] = "critical!",
|
||||
[LOG_WARNING] = "warning",
|
||||
[LOG_ERR] = "error",
|
||||
[LOG_INFO] = "info",
|
||||
[LOG_DEBUG] = "debug"
|
||||
};
|
||||
|
||||
|
||||
void udhcp_logging(int level, const char *fmt, ...)
|
||||
{
|
||||
va_list p;
|
||||
|
||||
va_start(p, fmt);
|
||||
if(!daemonized) {
|
||||
printf("%s, ", syslog_level_msg[level]);
|
||||
vprintf(fmt, p);
|
||||
putchar('\n');
|
||||
}
|
||||
va_end(p);
|
||||
}
|
||||
|
||||
|
||||
void start_log_and_pid(const char *client_server, const char *pidfile)
|
||||
{
|
||||
int pid_fd;
|
||||
|
||||
/* Make sure our syslog fd isn't overwritten */
|
||||
sanitize_fds();
|
||||
|
||||
/* do some other misc startup stuff while we are here to save bytes */
|
||||
pid_fd = pidfile_acquire(pidfile);
|
||||
pidfile_write_release(pid_fd);
|
||||
|
||||
/* equivelent of doing a fflush after every \n */
|
||||
setlinebuf(stdout);
|
||||
|
||||
udhcp_logging(LOG_INFO, "%s (v%s) started", client_server, VERSION);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
/* common.h
|
||||
*
|
||||
* Russ Dill <Russ.Dill@asu.edu> September 2001
|
||||
* Rewritten by Vladimir Oleynik <dzo@simtreas.ru> (C) 2003
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef _COMMON_H
|
||||
#define _COMMON_H
|
||||
|
||||
#include "version.h"
|
||||
#include "libbb_udhcp.h"
|
||||
|
||||
|
||||
#ifndef UDHCP_SYSLOG
|
||||
enum syslog_levels {
|
||||
LOG_EMERG = 0,
|
||||
LOG_ALERT,
|
||||
LOG_CRIT,
|
||||
LOG_WARNING,
|
||||
LOG_ERR,
|
||||
LOG_INFO,
|
||||
LOG_DEBUG
|
||||
};
|
||||
#else
|
||||
#include <syslog.h>
|
||||
#endif
|
||||
|
||||
long uptime(void);
|
||||
void background(const char *pidfile);
|
||||
void start_log_and_pid(const char *client_server, const char *pidfile);
|
||||
void background(const char *pidfile);
|
||||
void udhcp_logging(int level, const char *fmt, ...);
|
||||
|
||||
#define LOG(level, str, args...) udhcp_logging(level, str, ## args)
|
||||
|
||||
#ifdef UDHCP_DEBUG
|
||||
# define DEBUG(level, str, args...) LOG(level, str, ## args)
|
||||
#else
|
||||
# define DEBUG(level, str, args...) do {;} while(0)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,467 @@
|
||||
#define _DEFAULT_SOURCE
|
||||
|
||||
#include "dhcp.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/if_packet.h>
|
||||
#include <net/if.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define DHCP_CLIENT_PORT 68
|
||||
#define DHCP_SERVER_PORT 67
|
||||
|
||||
#define DHCPDISCOVER 1
|
||||
#define DHCPOFFER 2
|
||||
#define DHCPREQUEST 3
|
||||
#define DHCPACK 5
|
||||
#define DHCPNAK 6
|
||||
|
||||
#define DHCP_MAGIC_COOKIE 0x63825363u
|
||||
#define DHCP_FLAGS_BROADCAST 0x8000
|
||||
|
||||
#define DHCP_OPTION_SUBNET_MASK 1
|
||||
#define DHCP_OPTION_ROUTER 3
|
||||
#define DHCP_OPTION_DNS 6
|
||||
#define DHCP_OPTION_REQUESTED_IP 50
|
||||
#define DHCP_OPTION_LEASE_TIME 51
|
||||
#define DHCP_OPTION_MSG_TYPE 53
|
||||
#define DHCP_OPTION_SERVER_IDENTIFIER 54
|
||||
#define DHCP_OPTION_PARAMETER_REQUEST 55
|
||||
#define DHCP_OPTION_END 255
|
||||
#define DHCP_OPTION_PAD 0
|
||||
|
||||
#define DHCP_HTYPE_ETHERNET 1
|
||||
#define DHCP_HLEN_ETHERNET 6
|
||||
#define DHCP_OP_BOOTREQUEST 1
|
||||
#define DHCP_OP_BOOTREPLY 2
|
||||
|
||||
#define DHCP_OPTIONS_SIZE 312
|
||||
#define DHCP_MIN_SIZE 300
|
||||
#define PKT_BUF_SIZE 1500
|
||||
|
||||
#pragma pack(push, 1)
|
||||
typedef struct {
|
||||
uint8_t op;
|
||||
uint8_t htype;
|
||||
uint8_t hlen;
|
||||
uint8_t hops;
|
||||
uint32_t xid;
|
||||
uint16_t secs;
|
||||
uint16_t flags;
|
||||
uint32_t ciaddr;
|
||||
uint32_t yiaddr;
|
||||
uint32_t siaddr;
|
||||
uint32_t giaddr;
|
||||
uint8_t chaddr[16];
|
||||
uint8_t sname[64];
|
||||
uint8_t file[128];
|
||||
uint32_t magic_cookie;
|
||||
uint8_t options[DHCP_OPTIONS_SIZE];
|
||||
} dhcp_packet_t;
|
||||
|
||||
typedef struct {
|
||||
uint16_t src;
|
||||
uint16_t dst;
|
||||
uint16_t len;
|
||||
uint16_t check;
|
||||
} sdhcp_udp_header_t;
|
||||
#pragma pack(pop)
|
||||
|
||||
static uint16_t ip_checksum(const void *data, size_t len)
|
||||
{
|
||||
const uint8_t *p = data;
|
||||
uint32_t sum = 0;
|
||||
|
||||
while (len > 1) {
|
||||
sum += ((uint16_t)p[0] << 8) | p[1];
|
||||
p += 2;
|
||||
len -= 2;
|
||||
}
|
||||
if (len) {
|
||||
sum += ((uint16_t)p[0] << 8);
|
||||
}
|
||||
while (sum >> 16) {
|
||||
sum = (sum & 0xffff) + (sum >> 16);
|
||||
}
|
||||
return (uint16_t)~sum;
|
||||
}
|
||||
|
||||
static uint32_t make_xid(void)
|
||||
{
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return (uint32_t)ts.tv_nsec ^ (uint32_t)ts.tv_sec ^ (uint32_t)getpid();
|
||||
}
|
||||
|
||||
static int get_mac(const char *ifname, uint8_t mac[6])
|
||||
{
|
||||
int fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (fd < 0) return -1;
|
||||
|
||||
struct ifreq ifr;
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
strncpy(ifr.ifr_name, ifname, IFNAMSIZ - 1);
|
||||
|
||||
if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(mac, ifr.ifr_hwaddr.sa_data, 6);
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int open_sock(const char *ifname, int *ifindex_out)
|
||||
{
|
||||
int ifindex = if_nametoindex(ifname);
|
||||
if (!ifindex) {
|
||||
errno = ENODEV;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int fd = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
|
||||
if (fd < 0) return -1;
|
||||
|
||||
struct sockaddr_ll addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sll_family = AF_PACKET;
|
||||
addr.sll_protocol = htons(ETH_P_IP);
|
||||
addr.sll_ifindex = ifindex;
|
||||
|
||||
if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*ifindex_out = ifindex;
|
||||
return fd;
|
||||
}
|
||||
|
||||
static size_t dhcp_init(dhcp_packet_t *d, uint32_t xid, const uint8_t mac[6], uint8_t msg_type)
|
||||
{
|
||||
memset(d, 0, sizeof(*d));
|
||||
d->op = DHCP_OP_BOOTREQUEST;
|
||||
d->htype = DHCP_HTYPE_ETHERNET;
|
||||
d->hlen = DHCP_HLEN_ETHERNET;
|
||||
d->xid = htonl(xid);
|
||||
d->flags = htons(DHCP_FLAGS_BROADCAST);
|
||||
memcpy(d->chaddr, mac, 6);
|
||||
d->magic_cookie = htonl(DHCP_MAGIC_COOKIE);
|
||||
|
||||
size_t o = 0;
|
||||
d->options[o++] = DHCP_OPTION_MSG_TYPE;
|
||||
d->options[o++] = 1;
|
||||
d->options[o++] = msg_type;
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
static size_t build_discover(dhcp_packet_t *d, uint32_t xid, const uint8_t mac[6])
|
||||
{
|
||||
size_t o = dhcp_init(d, xid, mac, DHCPDISCOVER);
|
||||
|
||||
uint8_t prl[] = {
|
||||
DHCP_OPTION_SUBNET_MASK,
|
||||
DHCP_OPTION_ROUTER,
|
||||
DHCP_OPTION_DNS,
|
||||
DHCP_OPTION_LEASE_TIME,
|
||||
DHCP_OPTION_SERVER_IDENTIFIER
|
||||
};
|
||||
|
||||
d->options[o++] = DHCP_OPTION_PARAMETER_REQUEST;
|
||||
d->options[o++] = sizeof(prl);
|
||||
memcpy(&d->options[o], prl, sizeof(prl));
|
||||
o += sizeof(prl);
|
||||
|
||||
d->options[o++] = DHCP_OPTION_END;
|
||||
|
||||
size_t len = offsetof(dhcp_packet_t, options) + o;
|
||||
return len < DHCP_MIN_SIZE ? DHCP_MIN_SIZE : len;
|
||||
}
|
||||
|
||||
static size_t build_request(dhcp_packet_t *d, uint32_t xid, const uint8_t mac[6],
|
||||
uint32_t req_ip, uint32_t server_id)
|
||||
{
|
||||
size_t o = dhcp_init(d, xid, mac, DHCPREQUEST);
|
||||
|
||||
d->options[o++] = DHCP_OPTION_REQUESTED_IP;
|
||||
d->options[o++] = 4;
|
||||
memcpy(&d->options[o], &req_ip, 4);
|
||||
o += 4;
|
||||
|
||||
d->options[o++] = DHCP_OPTION_SERVER_IDENTIFIER;
|
||||
d->options[o++] = 4;
|
||||
memcpy(&d->options[o], &server_id, 4);
|
||||
o += 4;
|
||||
|
||||
uint8_t prl[] = {
|
||||
DHCP_OPTION_SUBNET_MASK,
|
||||
DHCP_OPTION_ROUTER,
|
||||
DHCP_OPTION_DNS,
|
||||
DHCP_OPTION_LEASE_TIME
|
||||
};
|
||||
|
||||
d->options[o++] = DHCP_OPTION_PARAMETER_REQUEST;
|
||||
d->options[o++] = sizeof(prl);
|
||||
memcpy(&d->options[o], prl, sizeof(prl));
|
||||
o += sizeof(prl);
|
||||
|
||||
d->options[o++] = DHCP_OPTION_END;
|
||||
|
||||
size_t len = offsetof(dhcp_packet_t, options) + o;
|
||||
return len < DHCP_MIN_SIZE ? DHCP_MIN_SIZE : len;
|
||||
}
|
||||
|
||||
static int build_ip_udp(uint8_t *buf, size_t buflen,
|
||||
const dhcp_packet_t *dhcp, size_t dhcp_len,
|
||||
size_t *out_len)
|
||||
{
|
||||
size_t total = sizeof(struct iphdr) + sizeof(sdhcp_udp_header_t) + dhcp_len;
|
||||
if (buflen < total) {
|
||||
errno = EMSGSIZE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct iphdr *ip = (struct iphdr *)buf;
|
||||
sdhcp_udp_header_t *udp = (sdhcp_udp_header_t *)(buf + sizeof(struct iphdr));
|
||||
uint8_t *payload = buf + sizeof(struct iphdr) + sizeof(sdhcp_udp_header_t);
|
||||
|
||||
memset(buf, 0, sizeof(struct iphdr) + sizeof(sdhcp_udp_header_t));
|
||||
memcpy(payload, dhcp, dhcp_len);
|
||||
|
||||
ip->version = 4;
|
||||
ip->ihl = 5;
|
||||
ip->ttl = 64;
|
||||
ip->protocol = IPPROTO_UDP;
|
||||
ip->tot_len = htons(total);
|
||||
ip->saddr = htonl(INADDR_ANY);
|
||||
ip->daddr = htonl(INADDR_BROADCAST);
|
||||
ip->check = htons(ip_checksum(ip, sizeof(*ip)));
|
||||
|
||||
udp->src = htons(DHCP_CLIENT_PORT);
|
||||
udp->dst = htons(DHCP_SERVER_PORT);
|
||||
udp->len = htons(sizeof(sdhcp_udp_header_t) + dhcp_len);
|
||||
udp->check = 0; /* IPv4 allows zero UDP checksum */
|
||||
|
||||
*out_len = total;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int send_buf(int fd, int ifindex, const void *buf, size_t len)
|
||||
{
|
||||
struct sockaddr_ll dst;
|
||||
memset(&dst, 0, sizeof(dst));
|
||||
dst.sll_family = AF_PACKET;
|
||||
dst.sll_protocol = htons(ETH_P_IP);
|
||||
dst.sll_ifindex = ifindex;
|
||||
dst.sll_halen = ETH_ALEN;
|
||||
memset(dst.sll_addr, 0xff, ETH_ALEN);
|
||||
|
||||
ssize_t n = sendto(fd, buf, len, 0, (struct sockaddr *)&dst, sizeof(dst));
|
||||
return n == (ssize_t)len ? 0 : -1;
|
||||
}
|
||||
|
||||
static int parse_options(const dhcp_packet_t *d, size_t len,
|
||||
uint8_t *msg_type, sdhcp_lease_t *lease)
|
||||
{
|
||||
size_t base = offsetof(dhcp_packet_t, options);
|
||||
if (len < base) return -1;
|
||||
if (ntohl(d->magic_cookie) != DHCP_MAGIC_COOKIE) return -1;
|
||||
|
||||
*msg_type = 0;
|
||||
lease->subnet_mask.s_addr = 0;
|
||||
lease->router.s_addr = 0;
|
||||
lease->dns_count = 0;
|
||||
lease->lease_time = 0;
|
||||
lease->server_identifier = 0;
|
||||
|
||||
size_t i = 0, max = len - base;
|
||||
while (i < max) {
|
||||
uint8_t code = d->options[i++];
|
||||
if (code == DHCP_OPTION_PAD) continue;
|
||||
if (code == DHCP_OPTION_END) break;
|
||||
if (i >= max) return -1;
|
||||
|
||||
uint8_t optlen = d->options[i++];
|
||||
if ((size_t)optlen > max - i) return -1;
|
||||
|
||||
const uint8_t *v = &d->options[i];
|
||||
|
||||
switch (code) {
|
||||
case DHCP_OPTION_MSG_TYPE:
|
||||
if (optlen == 1) *msg_type = v[0];
|
||||
break;
|
||||
case DHCP_OPTION_SUBNET_MASK:
|
||||
if (optlen == 4) memcpy(&lease->subnet_mask.s_addr, v, 4);
|
||||
break;
|
||||
case DHCP_OPTION_ROUTER:
|
||||
if (optlen >= 4) memcpy(&lease->router.s_addr, v, 4);
|
||||
break;
|
||||
case DHCP_OPTION_DNS: {
|
||||
int count = optlen / 4;
|
||||
if (count > SDHCP_MAX_DNS) count = SDHCP_MAX_DNS;
|
||||
for (int j = 0; j < count; j++) {
|
||||
memcpy(&lease->dns[j].s_addr, v + j * 4, 4);
|
||||
}
|
||||
lease->dns_count = count;
|
||||
break;
|
||||
}
|
||||
case DHCP_OPTION_LEASE_TIME:
|
||||
if (optlen == 4) {
|
||||
uint32_t t;
|
||||
memcpy(&t, v, 4);
|
||||
lease->lease_time = ntohl(t);
|
||||
}
|
||||
break;
|
||||
case DHCP_OPTION_SERVER_IDENTIFIER:
|
||||
if (optlen == 4) memcpy(&lease->server_identifier, v, 4);
|
||||
break;
|
||||
}
|
||||
|
||||
i += optlen;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int recv_dhcp(int fd, uint32_t xid, const uint8_t mac[6], int timeout_ms,
|
||||
dhcp_packet_t *out, size_t *out_len, uint8_t *msg_type,
|
||||
sdhcp_lease_t *lease)
|
||||
{
|
||||
uint8_t buf[PKT_BUF_SIZE];
|
||||
struct timeval start, now;
|
||||
|
||||
gettimeofday(&start, NULL);
|
||||
|
||||
for (;;) {
|
||||
gettimeofday(&now, NULL);
|
||||
long elapsed = (now.tv_sec - start.tv_sec) * 1000L +
|
||||
(now.tv_usec - start.tv_usec) / 1000L;
|
||||
long remain = timeout_ms - elapsed;
|
||||
if (remain <= 0) {
|
||||
errno = ETIMEDOUT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
fd_set rfds;
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(fd, &rfds);
|
||||
|
||||
struct timeval tv;
|
||||
tv.tv_sec = remain / 1000;
|
||||
tv.tv_usec = (remain % 1000) * 1000;
|
||||
|
||||
int rc = select(fd + 1, &rfds, NULL, NULL, &tv);
|
||||
if (rc < 0) return -1;
|
||||
if (rc == 0) {
|
||||
errno = ETIMEDOUT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t n = recv(fd, buf, sizeof(buf), 0);
|
||||
if (n < (ssize_t)(sizeof(struct iphdr) + sizeof(sdhcp_udp_header_t))) continue;
|
||||
|
||||
struct iphdr *ip = (struct iphdr *)buf;
|
||||
if (ip->version != 4 || ip->ihl < 5 || ip->protocol != IPPROTO_UDP) continue;
|
||||
|
||||
size_t ip_hlen = ip->ihl * 4;
|
||||
if ((size_t)n < ip_hlen + sizeof(sdhcp_udp_header_t)) continue;
|
||||
|
||||
sdhcp_udp_header_t *udp = (sdhcp_udp_header_t *)(buf + ip_hlen);
|
||||
if (ntohs(udp->src) != DHCP_SERVER_PORT || ntohs(udp->dst) != DHCP_CLIENT_PORT) continue;
|
||||
|
||||
uint16_t udp_len = ntohs(udp->len);
|
||||
if (udp_len < sizeof(sdhcp_udp_header_t)) continue;
|
||||
|
||||
size_t dhcp_len = udp_len - sizeof(sdhcp_udp_header_t);
|
||||
if (dhcp_len < offsetof(dhcp_packet_t, options)) continue;
|
||||
if (dhcp_len > sizeof(*out)) continue;
|
||||
if ((size_t)n < ip_hlen + sizeof(sdhcp_udp_header_t) + dhcp_len) continue;
|
||||
|
||||
const dhcp_packet_t *d = (const dhcp_packet_t *)(buf + ip_hlen + sizeof(sdhcp_udp_header_t));
|
||||
|
||||
if (d->op != DHCP_OP_BOOTREPLY) continue;
|
||||
if (d->htype != DHCP_HTYPE_ETHERNET || d->hlen != DHCP_HLEN_ETHERNET) continue;
|
||||
if (ntohl(d->xid) != xid) continue;
|
||||
if (memcmp(d->chaddr, mac, 6) != 0) continue;
|
||||
|
||||
lease->yiaddr.s_addr = d->yiaddr;
|
||||
|
||||
if (parse_options(d, dhcp_len, msg_type, lease) < 0) continue;
|
||||
|
||||
memcpy(out, d, dhcp_len);
|
||||
*out_len = dhcp_len;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int sdhcp_acquire(const char *ifname, int timeout_ms, int retries, sdhcp_lease_t *lease)
|
||||
{
|
||||
if (!ifname || !lease || timeout_ms <= 0 || retries <= 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(lease, 0, sizeof(*lease));
|
||||
|
||||
uint8_t mac[6];
|
||||
if (get_mac(ifname, mac) < 0) return -1;
|
||||
|
||||
int ifindex;
|
||||
int fd = open_sock(ifname, &ifindex);
|
||||
if (fd < 0) return -1;
|
||||
|
||||
uint32_t xid = make_xid();
|
||||
dhcp_packet_t tx, rx;
|
||||
uint8_t buf[PKT_BUF_SIZE];
|
||||
size_t tx_len, rx_len;
|
||||
uint8_t msg_type;
|
||||
|
||||
for (int attempt = 0; attempt < retries; attempt++) {
|
||||
size_t dhcp_len = build_discover(&tx, xid, mac);
|
||||
if (build_ip_udp(buf, sizeof(buf), &tx, dhcp_len, &tx_len) < 0) break;
|
||||
if (send_buf(fd, ifindex, buf, tx_len) < 0) continue;
|
||||
|
||||
if (recv_dhcp(fd, xid, mac, timeout_ms, &rx, &rx_len, &msg_type, lease) < 0) {
|
||||
continue;
|
||||
}
|
||||
if (msg_type != DHCPOFFER) continue;
|
||||
if (!rx.yiaddr || !lease->server_identifier) continue;
|
||||
|
||||
dhcp_len = build_request(&tx, xid, mac, rx.yiaddr, lease->server_identifier);
|
||||
if (build_ip_udp(buf, sizeof(buf), &tx, dhcp_len, &tx_len) < 0) break;
|
||||
if (send_buf(fd, ifindex, buf, tx_len) < 0) continue;
|
||||
|
||||
if (recv_dhcp(fd, xid, mac, timeout_ms, &rx, &rx_len, &msg_type, lease) < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (msg_type == DHCPNAK) {
|
||||
errno = EPROTO;
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (msg_type == DHCPACK) {
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
close(fd);
|
||||
errno = ETIMEDOUT;
|
||||
return -1;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifndef DHCP_H
|
||||
#define DHCP_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#define SDHCP_MAX_DNS 4
|
||||
|
||||
typedef struct {
|
||||
struct in_addr yiaddr;
|
||||
struct in_addr subnet_mask;
|
||||
struct in_addr router;
|
||||
struct in_addr dns[SDHCP_MAX_DNS];
|
||||
int dns_count;
|
||||
uint32_t lease_time; /* host byte order */
|
||||
uint32_t server_identifier; /* network byte order */
|
||||
} sdhcp_lease_t;
|
||||
|
||||
// Simple one-shot DHCP acquisition for connection to Wii U
|
||||
//
|
||||
// Return 0 on success, -1 on error with errno set
|
||||
int sdhcp_acquire(const char *ifname, int timeout_ms, int retries, sdhcp_lease_t *lease);
|
||||
|
||||
#endif
|
||||
@@ -1,535 +0,0 @@
|
||||
/* dhcpc.c
|
||||
*
|
||||
* udhcp DHCP client
|
||||
*
|
||||
* Russ Dill <Russ.Dill@asu.edu> July 2001
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <sys/file.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <net/if.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "dhcpd.h"
|
||||
#include "dhcpc.h"
|
||||
#include "options.h"
|
||||
#include "clientpacket.h"
|
||||
#include "clientsocket.h"
|
||||
#include "script.h"
|
||||
#include "socket.h"
|
||||
#include "common.h"
|
||||
#include "signalpipe.h"
|
||||
|
||||
static int state;
|
||||
static unsigned long requested_ip; /* = 0 */
|
||||
static unsigned long server_addr;
|
||||
static unsigned long timeout;
|
||||
static int packet_num; /* = 0 */
|
||||
static int fd = -1;
|
||||
|
||||
#define LISTEN_NONE 0
|
||||
#define LISTEN_KERNEL 1
|
||||
#define LISTEN_RAW 2
|
||||
static int listen_mode;
|
||||
|
||||
struct client_config_t client_config = {
|
||||
/* Default options. */
|
||||
abort_if_no_lease: 0,
|
||||
foreground: 0,
|
||||
quit_after_lease: 0,
|
||||
background_if_no_lease: 0,
|
||||
interface: "eth0",
|
||||
pidfile: NULL,
|
||||
callback: NULL,
|
||||
clientid: NULL,
|
||||
hostname: NULL,
|
||||
fqdn: NULL,
|
||||
ifindex: 0,
|
||||
arp: "\0\0\0\0\0\0", /* appease gcc-3.0 */
|
||||
};
|
||||
|
||||
#ifndef IN_BUSYBOX
|
||||
static void __attribute__ ((noreturn)) show_usage(void)
|
||||
{
|
||||
printf(
|
||||
"Usage: udhcpc [OPTIONS]\n\n"
|
||||
" -c, --clientid=CLIENTID Client identifier\n"
|
||||
" -H, --hostname=HOSTNAME Client hostname\n"
|
||||
" -h Alias for -H\n"
|
||||
" -F, --fqdn=FQDN Client fully qualified domain name\n"
|
||||
" -f, --foreground Do not fork after getting lease\n"
|
||||
" -b, --background Fork to background if lease cannot be\n"
|
||||
" immediately negotiated.\n"
|
||||
" -i, --interface=INTERFACE Interface to use (default: eth0)\n"
|
||||
" -n, --now Exit with failure if lease cannot be\n"
|
||||
" immediately negotiated.\n"
|
||||
" -p, --pidfile=file Store process ID of daemon in file\n"
|
||||
" -q, --quit Quit after obtaining lease\n"
|
||||
" -r, --request=IP IP address to request (default: none)\n"
|
||||
" -s, --script=file Run file at dhcp events (default:\n"
|
||||
" " DEFAULT_SCRIPT ")\n"
|
||||
" -v, --version Display version\n"
|
||||
);
|
||||
exit(0);
|
||||
}
|
||||
#else
|
||||
#define show_usage bb_show_usage
|
||||
extern void show_usage(void) __attribute__ ((noreturn));
|
||||
#endif
|
||||
|
||||
|
||||
/* just a little helper */
|
||||
static void change_mode(int new_mode)
|
||||
{
|
||||
DEBUG(LOG_INFO, "entering %s listen mode",
|
||||
new_mode ? (new_mode == 1 ? "kernel" : "raw") : "none");
|
||||
if (fd >= 0) close(fd);
|
||||
fd = -1;
|
||||
listen_mode = new_mode;
|
||||
}
|
||||
|
||||
|
||||
/* perform a renew */
|
||||
static void perform_renew(void)
|
||||
{
|
||||
LOG(LOG_INFO, "Performing a DHCP renew");
|
||||
switch (state) {
|
||||
case BOUND:
|
||||
change_mode(LISTEN_KERNEL);
|
||||
case RENEWING:
|
||||
case REBINDING:
|
||||
state = RENEW_REQUESTED;
|
||||
break;
|
||||
case RENEW_REQUESTED: /* impatient are we? fine, square 1 */
|
||||
run_script(NULL, "deconfig");
|
||||
case REQUESTING:
|
||||
case RELEASED:
|
||||
change_mode(LISTEN_RAW);
|
||||
state = INIT_SELECTING;
|
||||
break;
|
||||
case INIT_SELECTING:
|
||||
break;
|
||||
}
|
||||
|
||||
/* start things over */
|
||||
packet_num = 0;
|
||||
|
||||
/* Kill any timeouts because the user wants this to hurry along */
|
||||
timeout = 0;
|
||||
}
|
||||
|
||||
|
||||
/* perform a release */
|
||||
static void perform_release(void)
|
||||
{
|
||||
char buffer[16];
|
||||
struct in_addr temp_addr;
|
||||
|
||||
/* send release packet */
|
||||
if (state == BOUND || state == RENEWING || state == REBINDING) {
|
||||
temp_addr.s_addr = server_addr;
|
||||
sprintf(buffer, "%s", inet_ntoa(temp_addr));
|
||||
temp_addr.s_addr = requested_ip;
|
||||
LOG(LOG_INFO, "Unicasting a release of %s to %s",
|
||||
inet_ntoa(temp_addr), buffer);
|
||||
send_release(server_addr, requested_ip); /* unicast */
|
||||
run_script(NULL, "deconfig");
|
||||
}
|
||||
LOG(LOG_INFO, "Entering released state");
|
||||
|
||||
change_mode(LISTEN_NONE);
|
||||
state = RELEASED;
|
||||
timeout = 0x7fffffff;
|
||||
}
|
||||
|
||||
|
||||
static void client_background(void)
|
||||
{
|
||||
background(client_config.pidfile);
|
||||
client_config.foreground = 1; /* Do not fork again. */
|
||||
client_config.background_if_no_lease = 0;
|
||||
}
|
||||
|
||||
|
||||
int udhcpc_main()
|
||||
{
|
||||
uint8_t *temp, *message;
|
||||
unsigned long t1 = 0, t2 = 0, xid = 0;
|
||||
unsigned long start = 0, lease;
|
||||
fd_set rfds;
|
||||
int retval;
|
||||
struct timeval tv;
|
||||
int c, len;
|
||||
struct dhcpMessage packet;
|
||||
struct in_addr temp_addr;
|
||||
long now;
|
||||
int max_fd;
|
||||
int sig;
|
||||
|
||||
static const struct option arg_options[] = {
|
||||
{"clientid", required_argument, 0, 'c'},
|
||||
{"foreground", no_argument, 0, 'f'},
|
||||
{"background", no_argument, 0, 'b'},
|
||||
{"hostname", required_argument, 0, 'H'},
|
||||
{"hostname", required_argument, 0, 'h'},
|
||||
{"fqdn", required_argument, 0, 'F'},
|
||||
{"interface", required_argument, 0, 'i'},
|
||||
{"now", no_argument, 0, 'n'},
|
||||
{"pidfile", required_argument, 0, 'p'},
|
||||
{"quit", no_argument, 0, 'q'},
|
||||
{"request", required_argument, 0, 'r'},
|
||||
{"script", required_argument, 0, 's'},
|
||||
{"version", no_argument, 0, 'v'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
/* get options */
|
||||
// while (1) {
|
||||
// int option_index = 0;
|
||||
// c = getopt_long(argc, argv, "c:fbH:h:F:i:np:qr:s:v", arg_options, &option_index);
|
||||
// if (c == -1) break;
|
||||
|
||||
// switch (c) {
|
||||
// case 'c':
|
||||
// len = strlen(optarg) > 255 ? 255 : strlen(optarg);
|
||||
// if (client_config.clientid) free(client_config.clientid);
|
||||
// client_config.clientid = xmalloc(len + 2);
|
||||
// client_config.clientid[OPT_CODE] = DHCP_CLIENT_ID;
|
||||
// client_config.clientid[OPT_LEN] = len;
|
||||
// client_config.clientid[OPT_DATA] = '\0';
|
||||
// strncpy(client_config.clientid + OPT_DATA, optarg, len);
|
||||
// break;
|
||||
// case 'f':
|
||||
// client_config.foreground = 1;
|
||||
// break;
|
||||
// case 'b':
|
||||
// client_config.background_if_no_lease = 1;
|
||||
// break;
|
||||
// case 'h':
|
||||
// case 'H':
|
||||
// len = strlen(optarg) > 255 ? 255 : strlen(optarg);
|
||||
// if (client_config.hostname) free(client_config.hostname);
|
||||
// client_config.hostname = xmalloc(len + 2);
|
||||
// client_config.hostname[OPT_CODE] = DHCP_HOST_NAME;
|
||||
// client_config.hostname[OPT_LEN] = len;
|
||||
// strncpy(client_config.hostname + 2, optarg, len);
|
||||
// break;
|
||||
// case 'F':
|
||||
// len = strlen(optarg) > 255 ? 255 : strlen(optarg);
|
||||
// if (client_config.fqdn) free(client_config.fqdn);
|
||||
// client_config.fqdn = xmalloc(len + 5);
|
||||
// client_config.fqdn[OPT_CODE] = DHCP_FQDN;
|
||||
// client_config.fqdn[OPT_LEN] = len + 3;
|
||||
// /* Flags: 0000NEOS
|
||||
// S: 1 => Client requests Server to update A RR in DNS as well as PTR
|
||||
// O: 1 => Server indicates to client that DNS has been updated regardless
|
||||
// E: 1 => Name data is DNS format, i.e. <4>host<6>domain<4>com<0> not "host.domain.com"
|
||||
// N: 1 => Client requests Server to not update DNS
|
||||
// */
|
||||
// client_config.fqdn[OPT_LEN + 1] = 0x1;
|
||||
// client_config.fqdn[OPT_LEN + 2] = 0;
|
||||
// client_config.fqdn[OPT_LEN + 3] = 0;
|
||||
// strncpy(client_config.fqdn + 5, optarg, len);
|
||||
// break;
|
||||
// case 'i':
|
||||
// client_config.interface = optarg;
|
||||
// break;
|
||||
// case 'n':
|
||||
// client_config.abort_if_no_lease = 1;
|
||||
// break;
|
||||
// case 'p':
|
||||
// client_config.pidfile = optarg;
|
||||
// break;
|
||||
// case 'q':
|
||||
// client_config.quit_after_lease = 1;
|
||||
// break;
|
||||
// case 'r':
|
||||
// requested_ip = inet_addr(optarg);
|
||||
// break;
|
||||
// case 's':
|
||||
// client_config.script = optarg;
|
||||
// break;
|
||||
// case 'v':
|
||||
// printf("udhcpcd, version %s\n\n", VERSION);
|
||||
// return 0;
|
||||
// break;
|
||||
// default:
|
||||
// show_usage();
|
||||
// }
|
||||
// }
|
||||
|
||||
/* Start the log, sanitize fd's, and write a pid file */
|
||||
start_log_and_pid("udhcpc", client_config.pidfile);
|
||||
|
||||
if (read_interface(client_config.interface, &client_config.ifindex,
|
||||
NULL, client_config.arp) < 0)
|
||||
return 1;
|
||||
|
||||
if (!client_config.clientid) {
|
||||
client_config.clientid = xmalloc(6 + 3);
|
||||
client_config.clientid[OPT_CODE] = DHCP_CLIENT_ID;
|
||||
client_config.clientid[OPT_LEN] = 7;
|
||||
client_config.clientid[OPT_DATA] = 1;
|
||||
memcpy(client_config.clientid + 3, client_config.arp, 6);
|
||||
}
|
||||
|
||||
/* setup the signal pipe */
|
||||
udhcp_sp_setup();
|
||||
|
||||
state = INIT_SELECTING;
|
||||
run_script(NULL, "deconfig");
|
||||
change_mode(LISTEN_RAW);
|
||||
|
||||
timeout = 0;
|
||||
|
||||
for (;;) {
|
||||
|
||||
tv.tv_sec = timeout - uptime();
|
||||
tv.tv_usec = 0;
|
||||
|
||||
if (listen_mode != LISTEN_NONE && fd < 0) {
|
||||
if (listen_mode == LISTEN_KERNEL)
|
||||
fd = listen_socket(INADDR_ANY, CLIENT_PORT, client_config.interface);
|
||||
else
|
||||
fd = raw_socket(client_config.ifindex);
|
||||
if (fd < 0) {
|
||||
LOG(LOG_ERR, "FATAL: couldn't listen on socket, %m");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
max_fd = udhcp_sp_fd_set(&rfds, fd);
|
||||
|
||||
if (tv.tv_sec > 0) {
|
||||
DEBUG(LOG_INFO, "Waiting on select...");
|
||||
retval = select(max_fd + 1, &rfds, NULL, NULL, &tv);
|
||||
} else retval = 0; /* If we already timed out, fall through */
|
||||
|
||||
now = uptime();
|
||||
if (retval == 0) {
|
||||
/* timeout dropped to zero */
|
||||
switch (state) {
|
||||
case INIT_SELECTING:
|
||||
if (packet_num < 3) {
|
||||
if (packet_num == 0)
|
||||
xid = random_xid();
|
||||
|
||||
/* send discover packet */
|
||||
send_discover(xid, requested_ip); /* broadcast */
|
||||
|
||||
timeout = now + ((packet_num == 2) ? 4 : 2);
|
||||
packet_num++;
|
||||
} else {
|
||||
run_script(NULL, "leasefail");
|
||||
if (client_config.background_if_no_lease) {
|
||||
LOG(LOG_INFO, "No lease, forking to background.");
|
||||
client_background();
|
||||
} else if (client_config.abort_if_no_lease) {
|
||||
LOG(LOG_INFO, "No lease, failing.");
|
||||
return 1;
|
||||
}
|
||||
/* wait to try again */
|
||||
packet_num = 0;
|
||||
timeout = now + 60;
|
||||
}
|
||||
break;
|
||||
case RENEW_REQUESTED:
|
||||
case REQUESTING:
|
||||
if (packet_num < 3) {
|
||||
/* send request packet */
|
||||
if (state == RENEW_REQUESTED)
|
||||
send_renew(xid, server_addr, requested_ip); /* unicast */
|
||||
else send_selecting(xid, server_addr, requested_ip); /* broadcast */
|
||||
|
||||
timeout = now + ((packet_num == 2) ? 10 : 2);
|
||||
packet_num++;
|
||||
} else {
|
||||
/* timed out, go back to init state */
|
||||
if (state == RENEW_REQUESTED) run_script(NULL, "deconfig");
|
||||
state = INIT_SELECTING;
|
||||
timeout = now;
|
||||
packet_num = 0;
|
||||
change_mode(LISTEN_RAW);
|
||||
}
|
||||
break;
|
||||
case BOUND:
|
||||
/* Lease is starting to run out, time to enter renewing state */
|
||||
state = RENEWING;
|
||||
change_mode(LISTEN_KERNEL);
|
||||
DEBUG(LOG_INFO, "Entering renew state");
|
||||
/* fall right through */
|
||||
case RENEWING:
|
||||
/* Either set a new T1, or enter REBINDING state */
|
||||
if ((t2 - t1) <= (lease / 14400 + 1)) {
|
||||
/* timed out, enter rebinding state */
|
||||
state = REBINDING;
|
||||
timeout = now + (t2 - t1);
|
||||
DEBUG(LOG_INFO, "Entering rebinding state");
|
||||
} else {
|
||||
/* send a request packet */
|
||||
send_renew(xid, server_addr, requested_ip); /* unicast */
|
||||
|
||||
t1 = (t2 - t1) / 2 + t1;
|
||||
timeout = t1 + start;
|
||||
}
|
||||
break;
|
||||
case REBINDING:
|
||||
/* Either set a new T2, or enter INIT state */
|
||||
if ((lease - t2) <= (lease / 14400 + 1)) {
|
||||
/* timed out, enter init state */
|
||||
state = INIT_SELECTING;
|
||||
LOG(LOG_INFO, "Lease lost, entering init state");
|
||||
run_script(NULL, "deconfig");
|
||||
timeout = now;
|
||||
packet_num = 0;
|
||||
change_mode(LISTEN_RAW);
|
||||
} else {
|
||||
/* send a request packet */
|
||||
send_renew(xid, 0, requested_ip); /* broadcast */
|
||||
|
||||
t2 = (lease - t2) / 2 + t2;
|
||||
timeout = t2 + start;
|
||||
}
|
||||
break;
|
||||
case RELEASED:
|
||||
/* yah, I know, *you* say it would never happen */
|
||||
timeout = 0x7fffffff;
|
||||
break;
|
||||
}
|
||||
} else if (retval > 0 && listen_mode != LISTEN_NONE && FD_ISSET(fd, &rfds)) {
|
||||
/* a packet is ready, read it */
|
||||
|
||||
if (listen_mode == LISTEN_KERNEL)
|
||||
len = get_packet(&packet, fd);
|
||||
else len = get_raw_packet(&packet, fd);
|
||||
|
||||
if (len == -1 && errno != EINTR) {
|
||||
DEBUG(LOG_INFO, "error on read, %m, reopening socket");
|
||||
change_mode(listen_mode); /* just close and reopen */
|
||||
}
|
||||
if (len < 0) continue;
|
||||
|
||||
if (packet.xid != xid) {
|
||||
DEBUG(LOG_INFO, "Ignoring XID %lx (our xid is %lx)",
|
||||
(unsigned long) packet.xid, xid);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((message = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) {
|
||||
DEBUG(LOG_ERR, "couldnt get option from packet -- ignoring");
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (state) {
|
||||
case INIT_SELECTING:
|
||||
/* Must be a DHCPOFFER to one of our xid's */
|
||||
if (*message == DHCPOFFER) {
|
||||
if ((temp = get_option(&packet, DHCP_SERVER_ID))) {
|
||||
memcpy(&server_addr, temp, 4);
|
||||
xid = packet.xid;
|
||||
requested_ip = packet.yiaddr;
|
||||
|
||||
/* enter requesting state */
|
||||
state = REQUESTING;
|
||||
timeout = now;
|
||||
packet_num = 0;
|
||||
} else {
|
||||
DEBUG(LOG_ERR, "No server ID in message");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RENEW_REQUESTED:
|
||||
case REQUESTING:
|
||||
case RENEWING:
|
||||
case REBINDING:
|
||||
if (*message == DHCPACK) {
|
||||
if (!(temp = get_option(&packet, DHCP_LEASE_TIME))) {
|
||||
LOG(LOG_ERR, "No lease time with ACK, using 1 hour lease");
|
||||
lease = 60 * 60;
|
||||
} else {
|
||||
memcpy(&lease, temp, 4);
|
||||
lease = ntohl(lease);
|
||||
}
|
||||
|
||||
/* enter bound state */
|
||||
t1 = lease / 2;
|
||||
|
||||
/* little fixed point for n * .875 */
|
||||
t2 = (lease * 0x7) >> 3;
|
||||
temp_addr.s_addr = packet.yiaddr;
|
||||
LOG(LOG_INFO, "Lease of %s obtained, lease time %ld",
|
||||
inet_ntoa(temp_addr), lease);
|
||||
start = now;
|
||||
timeout = t1 + start;
|
||||
requested_ip = packet.yiaddr;
|
||||
run_script(&packet,
|
||||
((state == RENEWING || state == REBINDING) ? "renew" : "bound"));
|
||||
|
||||
state = BOUND;
|
||||
change_mode(LISTEN_NONE);
|
||||
if (client_config.quit_after_lease)
|
||||
return 0;
|
||||
if (!client_config.foreground)
|
||||
client_background();
|
||||
|
||||
} else if (*message == DHCPNAK) {
|
||||
/* return to init state */
|
||||
LOG(LOG_INFO, "Received DHCP NAK");
|
||||
run_script(&packet, "nak");
|
||||
if (state != REQUESTING)
|
||||
run_script(NULL, "deconfig");
|
||||
state = INIT_SELECTING;
|
||||
timeout = now;
|
||||
requested_ip = 0;
|
||||
packet_num = 0;
|
||||
change_mode(LISTEN_RAW);
|
||||
sleep(3); /* avoid excessive network traffic */
|
||||
}
|
||||
break;
|
||||
/* case BOUND, RELEASED: - ignore all packets */
|
||||
}
|
||||
} else if (retval > 0 && (sig = udhcp_sp_read(&rfds))) {
|
||||
switch (sig) {
|
||||
case SIGUSR1:
|
||||
perform_renew();
|
||||
break;
|
||||
case SIGUSR2:
|
||||
perform_release();
|
||||
break;
|
||||
case SIGTERM:
|
||||
LOG(LOG_INFO, "Received SIGTERM");
|
||||
return 0;
|
||||
}
|
||||
} else if (retval == -1 && errno == EINTR) {
|
||||
/* a signal was caught */
|
||||
} else {
|
||||
/* An error occured */
|
||||
DEBUG(LOG_ERR, "Error on select");
|
||||
}
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/* dhcpc.h */
|
||||
#ifndef _DHCPC_H
|
||||
#define _DHCPC_H
|
||||
|
||||
#define DEFAULT_SCRIPT "/etc/udhcpc/default.script"
|
||||
|
||||
/* allow libbb_udhcp.h to redefine DEFAULT_SCRIPT */
|
||||
#include "libbb_udhcp.h"
|
||||
|
||||
#define INIT_SELECTING 0
|
||||
#define REQUESTING 1
|
||||
#define BOUND 2
|
||||
#define RENEWING 3
|
||||
#define REBINDING 4
|
||||
#define INIT_REBOOT 5
|
||||
#define RENEW_REQUESTED 6
|
||||
#define RELEASED 7
|
||||
|
||||
typedef void(*udhcpc_callback_t)(const char *name, char **envp, void *data);
|
||||
|
||||
struct client_config_t {
|
||||
char foreground; /* Do not fork */
|
||||
char quit_after_lease; /* Quit after obtaining lease */
|
||||
char abort_if_no_lease; /* Abort if no lease */
|
||||
char background_if_no_lease; /* Fork to background if no lease */
|
||||
char *interface; /* The name of the interface to use */
|
||||
char *pidfile; /* Optionally store the process ID */
|
||||
udhcpc_callback_t callback; /* User callback to run at dhcp events */
|
||||
void *callback_data; /* User callback to run at dhcp events */
|
||||
uint8_t *clientid; /* Optional client id to use */
|
||||
uint8_t *hostname; /* Optional hostname to use */
|
||||
uint8_t *fqdn; /* Optional fully qualified domain name to use */
|
||||
int ifindex; /* Index number of the interface to use */
|
||||
uint8_t arp[6]; /* Our arp address */
|
||||
};
|
||||
|
||||
extern struct client_config_t client_config;
|
||||
|
||||
extern int udhcpc_main();
|
||||
|
||||
#endif
|
||||
@@ -1,141 +0,0 @@
|
||||
/* dhcpd.h */
|
||||
#ifndef _DHCPD_H
|
||||
#define _DHCPD_H
|
||||
|
||||
#include <netinet/ip.h>
|
||||
#include <netinet/udp.h>
|
||||
|
||||
#include "libbb_udhcp.h"
|
||||
#include "leases.h"
|
||||
#include "version.h"
|
||||
|
||||
/************************************/
|
||||
/* Defaults _you_ may want to tweak */
|
||||
/************************************/
|
||||
|
||||
/* the period of time the client is allowed to use that address */
|
||||
#define LEASE_TIME (60*60*24*10) /* 10 days of seconds */
|
||||
#define LEASES_FILE "/var/lib/misc/udhcpd.leases"
|
||||
|
||||
/* where to find the DHCP server configuration file */
|
||||
#define DHCPD_CONF_FILE "/etc/udhcpd.conf"
|
||||
|
||||
/*****************************************************************/
|
||||
/* Do not modify below here unless you know what you are doing!! */
|
||||
/*****************************************************************/
|
||||
|
||||
/* DHCP protocol -- see RFC 2131 */
|
||||
#define SERVER_PORT 67
|
||||
#define CLIENT_PORT 68
|
||||
|
||||
#define DHCP_MAGIC 0x63825363
|
||||
|
||||
/* DHCP option codes (partial list) */
|
||||
#define DHCP_PADDING 0x00
|
||||
#define DHCP_SUBNET 0x01
|
||||
#define DHCP_TIME_OFFSET 0x02
|
||||
#define DHCP_ROUTER 0x03
|
||||
#define DHCP_TIME_SERVER 0x04
|
||||
#define DHCP_NAME_SERVER 0x05
|
||||
#define DHCP_DNS_SERVER 0x06
|
||||
#define DHCP_LOG_SERVER 0x07
|
||||
#define DHCP_COOKIE_SERVER 0x08
|
||||
#define DHCP_LPR_SERVER 0x09
|
||||
#define DHCP_HOST_NAME 0x0c
|
||||
#define DHCP_BOOT_SIZE 0x0d
|
||||
#define DHCP_DOMAIN_NAME 0x0f
|
||||
#define DHCP_SWAP_SERVER 0x10
|
||||
#define DHCP_ROOT_PATH 0x11
|
||||
#define DHCP_IP_TTL 0x17
|
||||
#define DHCP_MTU 0x1a
|
||||
#define DHCP_BROADCAST 0x1c
|
||||
#define DHCP_NTP_SERVER 0x2a
|
||||
#define DHCP_WINS_SERVER 0x2c
|
||||
#define DHCP_REQUESTED_IP 0x32
|
||||
#define DHCP_LEASE_TIME 0x33
|
||||
#define DHCP_OPTION_OVER 0x34
|
||||
#define DHCP_MESSAGE_TYPE 0x35
|
||||
#define DHCP_SERVER_ID 0x36
|
||||
#define DHCP_PARAM_REQ 0x37
|
||||
#define DHCP_MESSAGE 0x38
|
||||
#define DHCP_MAX_SIZE 0x39
|
||||
#define DHCP_T1 0x3a
|
||||
#define DHCP_T2 0x3b
|
||||
#define DHCP_VENDOR 0x3c
|
||||
#define DHCP_CLIENT_ID 0x3d
|
||||
#define DHCP_FQDN 0x51
|
||||
|
||||
#define DHCP_END 0xFF
|
||||
|
||||
|
||||
#define BOOTREQUEST 1
|
||||
#define BOOTREPLY 2
|
||||
|
||||
#define ETH_10MB 1
|
||||
#define ETH_10MB_LEN 6
|
||||
|
||||
#define DHCPDISCOVER 1
|
||||
#define DHCPOFFER 2
|
||||
#define DHCPREQUEST 3
|
||||
#define DHCPDECLINE 4
|
||||
#define DHCPACK 5
|
||||
#define DHCPNAK 6
|
||||
#define DHCPRELEASE 7
|
||||
#define DHCPINFORM 8
|
||||
|
||||
#define BROADCAST_FLAG 0x8000
|
||||
|
||||
#define OPTION_FIELD 0
|
||||
#define FILE_FIELD 1
|
||||
#define SNAME_FIELD 2
|
||||
|
||||
/* miscellaneous defines */
|
||||
#define MAC_BCAST_ADDR (uint8_t *) "\xff\xff\xff\xff\xff\xff"
|
||||
#define OPT_CODE 0
|
||||
#define OPT_LEN 1
|
||||
#define OPT_DATA 2
|
||||
|
||||
struct option_set {
|
||||
uint8_t *data;
|
||||
struct option_set *next;
|
||||
};
|
||||
|
||||
struct static_lease {
|
||||
uint8_t *mac;
|
||||
uint32_t *ip;
|
||||
struct static_lease *next;
|
||||
};
|
||||
|
||||
struct server_config_t {
|
||||
uint32_t server; /* Our IP, in network order */
|
||||
uint32_t start; /* Start address of leases, network order */
|
||||
uint32_t end; /* End of leases, network order */
|
||||
struct option_set *options; /* List of DHCP options loaded from the config file */
|
||||
char *interface; /* The name of the interface to use */
|
||||
int ifindex; /* Index number of the interface to use */
|
||||
uint8_t arp[6]; /* Our arp address */
|
||||
unsigned long lease; /* lease time in seconds (host order) */
|
||||
unsigned long max_leases; /* maximum number of leases (including reserved address) */
|
||||
char remaining; /* should the lease file be interpreted as lease time remaining, or
|
||||
* as the time the lease expires */
|
||||
unsigned long auto_time; /* how long should udhcpd wait before writing a config file.
|
||||
* if this is zero, it will only write one on SIGUSR1 */
|
||||
unsigned long decline_time; /* how long an address is reserved if a client returns a
|
||||
* decline message */
|
||||
unsigned long conflict_time; /* how long an arp conflict offender is leased for */
|
||||
unsigned long offer_time; /* how long an offered address is reserved */
|
||||
unsigned long min_lease; /* minimum lease a client can request*/
|
||||
char *lease_file;
|
||||
char *pidfile;
|
||||
char *notify_file; /* What to run whenever leases are written */
|
||||
uint32_t siaddr; /* next server bootp option */
|
||||
char *sname; /* bootp server name */
|
||||
char *boot_file; /* bootp boot file option */
|
||||
struct static_lease *static_leases; /* List of ip/mac pairs to assign static leases */
|
||||
};
|
||||
|
||||
extern struct server_config_t server_config;
|
||||
extern struct dhcpOfferedAddr *leases;
|
||||
|
||||
|
||||
#endif
|
||||
@@ -1,17 +0,0 @@
|
||||
/* files.h */
|
||||
#ifndef _FILES_H
|
||||
#define _FILES_H
|
||||
|
||||
struct config_keyword {
|
||||
const char *keyword;
|
||||
int (* const handler)(const char *line, void *var);
|
||||
void *var;
|
||||
const char *def;
|
||||
};
|
||||
|
||||
|
||||
int read_config(const char *file);
|
||||
void write_leases(void);
|
||||
void read_leases(const char *file);
|
||||
|
||||
#endif
|
||||
@@ -1,23 +0,0 @@
|
||||
/* leases.h */
|
||||
#ifndef _LEASES_H
|
||||
#define _LEASES_H
|
||||
|
||||
|
||||
struct dhcpOfferedAddr {
|
||||
uint8_t chaddr[16];
|
||||
uint32_t yiaddr; /* network order */
|
||||
uint32_t expires; /* host order */
|
||||
};
|
||||
|
||||
extern uint8_t blank_chaddr[];
|
||||
|
||||
void clear_lease(uint8_t *chaddr, uint32_t yiaddr);
|
||||
struct dhcpOfferedAddr *add_lease(uint8_t *chaddr, uint32_t yiaddr, unsigned long lease);
|
||||
int lease_expired(struct dhcpOfferedAddr *lease);
|
||||
struct dhcpOfferedAddr *oldest_expired_lease(void);
|
||||
struct dhcpOfferedAddr *find_lease_by_chaddr(uint8_t *chaddr);
|
||||
struct dhcpOfferedAddr *find_lease_by_yiaddr(uint32_t yiaddr);
|
||||
uint32_t find_address(int check_expired);
|
||||
|
||||
|
||||
#endif
|
||||
@@ -1,54 +0,0 @@
|
||||
/* libbb_udhcp.h - busybox compatability wrapper */
|
||||
|
||||
/* bit of a hack, do this no matter what the order of the includes.
|
||||
* (for busybox) */
|
||||
|
||||
#ifdef CONFIG_INSTALL_NO_USR
|
||||
#undef DEFAULT_SCRIPT
|
||||
#define DEFAULT_SCRIPT "/share/udhcpc/default.script"
|
||||
#endif
|
||||
|
||||
#ifndef _LIBBB_UDHCP_H
|
||||
#define _LIBBB_UDHCP_H
|
||||
|
||||
#ifdef IN_BUSYBOX
|
||||
#include "busybox.h"
|
||||
|
||||
#ifdef CONFIG_FEATURE_UDHCP_SYSLOG
|
||||
#define UDHCP_SYSLOG
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_FEATURE_UDHCP_DEBUG
|
||||
#define UDHCP_DEBUG
|
||||
#endif
|
||||
|
||||
#define COMBINED_BINARY
|
||||
#include "version.h"
|
||||
|
||||
#define xfopen bb_xfopen
|
||||
|
||||
#else /* ! BB_VER */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/sysinfo.h>
|
||||
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
|
||||
#define xmalloc malloc
|
||||
#define xcalloc calloc
|
||||
|
||||
static inline FILE *xfopen(const char *file, const char *mode)
|
||||
{
|
||||
FILE *fp;
|
||||
if (!(fp = fopen(file, mode))) {
|
||||
perror("could not open input file");
|
||||
exit(0);
|
||||
}
|
||||
return fp;
|
||||
}
|
||||
|
||||
#endif /* BB_VER */
|
||||
|
||||
#endif /* _LIBBB_UDHCP_H */
|
||||
@@ -1,234 +0,0 @@
|
||||
/*
|
||||
* options.c -- DHCP server option packet tools
|
||||
* Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "dhcpd.h"
|
||||
#include "files.h"
|
||||
#include "options.h"
|
||||
#include "common.h"
|
||||
|
||||
|
||||
/* supported options are easily added here */
|
||||
struct dhcp_option dhcp_options[] = {
|
||||
/* name[10] flags code */
|
||||
{"subnet", OPTION_IP | OPTION_REQ, 0x01},
|
||||
{"timezone", OPTION_S32, 0x02},
|
||||
{"router", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x03},
|
||||
{"timesvr", OPTION_IP | OPTION_LIST, 0x04},
|
||||
{"namesvr", OPTION_IP | OPTION_LIST, 0x05},
|
||||
{"dns", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x06},
|
||||
{"logsvr", OPTION_IP | OPTION_LIST, 0x07},
|
||||
{"cookiesvr", OPTION_IP | OPTION_LIST, 0x08},
|
||||
{"lprsvr", OPTION_IP | OPTION_LIST, 0x09},
|
||||
{"hostname", OPTION_STRING | OPTION_REQ, 0x0c},
|
||||
{"bootsize", OPTION_U16, 0x0d},
|
||||
{"domain", OPTION_STRING | OPTION_REQ, 0x0f},
|
||||
{"swapsvr", OPTION_IP, 0x10},
|
||||
//We had the possibility for the client to ask for a rootpath
|
||||
//closes http://bugs.debian.org/279110 (ericvb@debian.org)
|
||||
//{"rootpath", OPTION_STRING, 0x11},
|
||||
{"rootpath", OPTION_STRING | OPTION_REQ, 0x11},
|
||||
{"ipttl", OPTION_U8, 0x17},
|
||||
{"mtu", OPTION_U16, 0x1a},
|
||||
{"broadcast", OPTION_IP | OPTION_REQ, 0x1c},
|
||||
{"nisdomain", OPTION_STRING | OPTION_REQ, 0x28},
|
||||
{"nissrv", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x29},
|
||||
{"ntpsrv", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x2a},
|
||||
{"wins", OPTION_IP | OPTION_LIST, 0x2c},
|
||||
{"requestip", OPTION_IP, 0x32},
|
||||
{"lease", OPTION_U32, 0x33},
|
||||
{"dhcptype", OPTION_U8, 0x35},
|
||||
{"serverid", OPTION_IP, 0x36},
|
||||
{"message", OPTION_STRING, 0x38},
|
||||
{"tftp", OPTION_STRING, 0x42},
|
||||
{"bootfile", OPTION_STRING, 0x43},
|
||||
{"wpad", OPTION_STRING, 0xfc},
|
||||
{"", 0x00, 0x00}
|
||||
};
|
||||
|
||||
/* Lengths of the different option types */
|
||||
int option_lengths[] = {
|
||||
[OPTION_IP] = 4,
|
||||
[OPTION_IP_PAIR] = 8,
|
||||
[OPTION_BOOLEAN] = 1,
|
||||
[OPTION_STRING] = 1,
|
||||
[OPTION_U8] = 1,
|
||||
[OPTION_U16] = 2,
|
||||
[OPTION_S16] = 2,
|
||||
[OPTION_U32] = 4,
|
||||
[OPTION_S32] = 4
|
||||
};
|
||||
|
||||
|
||||
/* get an option with bounds checking (warning, not aligned). */
|
||||
uint8_t *get_option(struct dhcpMessage *packet, int code)
|
||||
{
|
||||
int i, length;
|
||||
uint8_t *optionptr;
|
||||
int over = 0, done = 0, curr = OPTION_FIELD;
|
||||
|
||||
optionptr = packet->options;
|
||||
i = 0;
|
||||
length = 308;
|
||||
while (!done) {
|
||||
if (i >= length) {
|
||||
LOG(LOG_WARNING, "bogus packet, option fields too long.");
|
||||
return NULL;
|
||||
}
|
||||
if (optionptr[i + OPT_CODE] == code) {
|
||||
if (i + 1 + optionptr[i + OPT_LEN] >= length) {
|
||||
LOG(LOG_WARNING, "bogus packet, option fields too long.");
|
||||
return NULL;
|
||||
}
|
||||
return optionptr + i + 2;
|
||||
}
|
||||
switch (optionptr[i + OPT_CODE]) {
|
||||
case DHCP_PADDING:
|
||||
i++;
|
||||
break;
|
||||
case DHCP_OPTION_OVER:
|
||||
if (i + 1 + optionptr[i + OPT_LEN] >= length) {
|
||||
LOG(LOG_WARNING, "bogus packet, option fields too long.");
|
||||
return NULL;
|
||||
}
|
||||
over = optionptr[i + 3];
|
||||
i += optionptr[OPT_LEN] + 2;
|
||||
break;
|
||||
case DHCP_END:
|
||||
if (curr == OPTION_FIELD && over & FILE_FIELD) {
|
||||
optionptr = packet->file;
|
||||
i = 0;
|
||||
length = 128;
|
||||
curr = FILE_FIELD;
|
||||
} else if (curr == FILE_FIELD && over & SNAME_FIELD) {
|
||||
optionptr = packet->sname;
|
||||
i = 0;
|
||||
length = 64;
|
||||
curr = SNAME_FIELD;
|
||||
} else done = 1;
|
||||
break;
|
||||
default:
|
||||
i += optionptr[OPT_LEN + i] + 2;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* return the position of the 'end' option (no bounds checking) */
|
||||
int end_option(uint8_t *optionptr)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
while (optionptr[i] != DHCP_END) {
|
||||
if (optionptr[i] == DHCP_PADDING) i++;
|
||||
else i += optionptr[i + OPT_LEN] + 2;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
/* add an option string to the options (an option string contains an option code,
|
||||
* length, then data) */
|
||||
int add_option_string(uint8_t *optionptr, uint8_t *string)
|
||||
{
|
||||
int end = end_option(optionptr);
|
||||
|
||||
/* end position + string length + option code/length + end option */
|
||||
if (end + string[OPT_LEN] + 2 + 1 >= 308) {
|
||||
LOG(LOG_ERR, "Option 0x%02x did not fit into the packet!", string[OPT_CODE]);
|
||||
return 0;
|
||||
}
|
||||
DEBUG(LOG_INFO, "adding option 0x%02x", string[OPT_CODE]);
|
||||
memcpy(optionptr + end, string, string[OPT_LEN] + 2);
|
||||
optionptr[end + string[OPT_LEN] + 2] = DHCP_END;
|
||||
return string[OPT_LEN] + 2;
|
||||
}
|
||||
|
||||
|
||||
/* add a one to four byte option to a packet */
|
||||
int add_simple_option(uint8_t *optionptr, uint8_t code, uint32_t data)
|
||||
{
|
||||
char length = 0;
|
||||
int i;
|
||||
uint8_t option[2 + 4];
|
||||
uint8_t *u8;
|
||||
uint16_t *u16;
|
||||
uint32_t *u32;
|
||||
uint32_t aligned;
|
||||
u8 = (uint8_t *) &aligned;
|
||||
u16 = (uint16_t *) &aligned;
|
||||
u32 = &aligned;
|
||||
|
||||
for (i = 0; dhcp_options[i].code; i++)
|
||||
if (dhcp_options[i].code == code) {
|
||||
length = option_lengths[dhcp_options[i].flags & TYPE_MASK];
|
||||
}
|
||||
|
||||
if (!length) {
|
||||
DEBUG(LOG_ERR, "Could not add option 0x%02x", code);
|
||||
return 0;
|
||||
}
|
||||
|
||||
option[OPT_CODE] = code;
|
||||
option[OPT_LEN] = length;
|
||||
|
||||
switch (length) {
|
||||
case 1: *u8 = data; break;
|
||||
case 2: *u16 = data; break;
|
||||
case 4: *u32 = data; break;
|
||||
}
|
||||
memcpy(option + 2, &aligned, length);
|
||||
return add_option_string(optionptr, option);
|
||||
}
|
||||
|
||||
|
||||
/* find option 'code' in opt_list */
|
||||
struct option_set *find_option(struct option_set *opt_list, char code)
|
||||
{
|
||||
while (opt_list && opt_list->data[OPT_CODE] < code)
|
||||
opt_list = opt_list->next;
|
||||
|
||||
if (opt_list && opt_list->data[OPT_CODE] == code) return opt_list;
|
||||
else return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* add an option to the opt_list */
|
||||
void attach_option(struct option_set **opt_list, struct dhcp_option *option, char *buffer, int length)
|
||||
{
|
||||
struct option_set *existing, *new, **curr;
|
||||
|
||||
/* add it to an existing option */
|
||||
if ((existing = find_option(*opt_list, option->code))) {
|
||||
DEBUG(LOG_INFO, "Attaching option %s to existing member of list", option->name);
|
||||
if (option->flags & OPTION_LIST) {
|
||||
if (existing->data[OPT_LEN] + length <= 255) {
|
||||
existing->data = realloc(existing->data,
|
||||
existing->data[OPT_LEN] + length + 2);
|
||||
memcpy(existing->data + existing->data[OPT_LEN] + 2, buffer, length);
|
||||
existing->data[OPT_LEN] += length;
|
||||
} /* else, ignore the data, we could put this in a second option in the future */
|
||||
} /* else, ignore the new data */
|
||||
} else {
|
||||
DEBUG(LOG_INFO, "Attaching option %s to list", option->name);
|
||||
|
||||
/* make a new option */
|
||||
new = xmalloc(sizeof(struct option_set));
|
||||
new->data = xmalloc(length + 2);
|
||||
new->data[OPT_CODE] = option->code;
|
||||
new->data[OPT_LEN] = length;
|
||||
memcpy(new->data + 2, buffer, length);
|
||||
|
||||
curr = opt_list;
|
||||
while (*curr && (*curr)->data[OPT_CODE] < option->code)
|
||||
curr = &(*curr)->next;
|
||||
|
||||
new->next = *curr;
|
||||
*curr = new;
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/* options.h */
|
||||
#ifndef _OPTIONS_H
|
||||
#define _OPTIONS_H
|
||||
|
||||
#include "packet.h"
|
||||
|
||||
#define TYPE_MASK 0x0F
|
||||
|
||||
enum {
|
||||
OPTION_IP=1,
|
||||
OPTION_IP_PAIR,
|
||||
OPTION_STRING,
|
||||
OPTION_BOOLEAN,
|
||||
OPTION_U8,
|
||||
OPTION_U16,
|
||||
OPTION_S16,
|
||||
OPTION_U32,
|
||||
OPTION_S32
|
||||
};
|
||||
|
||||
#define OPTION_REQ 0x10 /* have the client request this option */
|
||||
#define OPTION_LIST 0x20 /* There can be a list of 1 or more of these */
|
||||
|
||||
struct dhcp_option {
|
||||
char name[10];
|
||||
char flags;
|
||||
uint8_t code;
|
||||
};
|
||||
|
||||
extern struct dhcp_option dhcp_options[];
|
||||
extern int option_lengths[];
|
||||
|
||||
uint8_t *get_option(struct dhcpMessage *packet, int code);
|
||||
int end_option(uint8_t *optionptr);
|
||||
int add_option_string(uint8_t *optionptr, uint8_t *string);
|
||||
int add_simple_option(uint8_t *optionptr, uint8_t code, uint32_t data);
|
||||
struct option_set *find_option(struct option_set *opt_list, char code);
|
||||
void attach_option(struct option_set **opt_list, struct dhcp_option *option, char *buffer, int length);
|
||||
|
||||
#endif
|
||||
@@ -1,211 +0,0 @@
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <features.h>
|
||||
#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1
|
||||
#include <netpacket/packet.h>
|
||||
#include <net/ethernet.h>
|
||||
#else
|
||||
#include <asm/types.h>
|
||||
#include <linux/if_packet.h>
|
||||
#include <linux/if_ether.h>
|
||||
#endif
|
||||
#include <errno.h>
|
||||
|
||||
#include "packet.h"
|
||||
#include "dhcpd.h"
|
||||
#include "options.h"
|
||||
#include "common.h"
|
||||
|
||||
|
||||
void init_header(struct dhcpMessage *packet, char type)
|
||||
{
|
||||
memset(packet, 0, sizeof(struct dhcpMessage));
|
||||
switch (type) {
|
||||
case DHCPDISCOVER:
|
||||
case DHCPREQUEST:
|
||||
case DHCPRELEASE:
|
||||
case DHCPINFORM:
|
||||
packet->op = BOOTREQUEST;
|
||||
break;
|
||||
case DHCPOFFER:
|
||||
case DHCPACK:
|
||||
case DHCPNAK:
|
||||
packet->op = BOOTREPLY;
|
||||
}
|
||||
packet->htype = ETH_10MB;
|
||||
packet->hlen = ETH_10MB_LEN;
|
||||
packet->cookie = htonl(DHCP_MAGIC);
|
||||
packet->options[0] = DHCP_END;
|
||||
add_simple_option(packet->options, DHCP_MESSAGE_TYPE, type);
|
||||
}
|
||||
|
||||
|
||||
/* read a packet from socket fd, return -1 on read error, -2 on packet error */
|
||||
int get_packet(struct dhcpMessage *packet, int fd)
|
||||
{
|
||||
int bytes;
|
||||
int i;
|
||||
const char broken_vendors[][8] = {
|
||||
"MSFT 98",
|
||||
""
|
||||
};
|
||||
char unsigned *vendor;
|
||||
|
||||
memset(packet, 0, sizeof(struct dhcpMessage));
|
||||
bytes = read(fd, packet, sizeof(struct dhcpMessage));
|
||||
if (bytes < 0) {
|
||||
DEBUG(LOG_INFO, "couldn't read on listening socket, ignoring");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ntohl(packet->cookie) != DHCP_MAGIC) {
|
||||
LOG(LOG_ERR, "received bogus message, ignoring");
|
||||
return -2;
|
||||
}
|
||||
DEBUG(LOG_INFO, "Received a packet");
|
||||
|
||||
if (packet->op == BOOTREQUEST && (vendor = get_option(packet, DHCP_VENDOR))) {
|
||||
for (i = 0; broken_vendors[i][0]; i++) {
|
||||
if (vendor[OPT_LEN - 2] == (uint8_t) strlen(broken_vendors[i]) &&
|
||||
!strncmp(vendor, broken_vendors[i], vendor[OPT_LEN - 2])) {
|
||||
DEBUG(LOG_INFO, "broken client (%s), forcing broadcast",
|
||||
broken_vendors[i]);
|
||||
packet->flags |= htons(BROADCAST_FLAG);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
|
||||
uint16_t checksum(void *addr, int count)
|
||||
{
|
||||
/* Compute Internet Checksum for "count" bytes
|
||||
* beginning at location "addr".
|
||||
*/
|
||||
register int32_t sum = 0;
|
||||
uint16_t *source = (uint16_t *) addr;
|
||||
|
||||
while (count > 1) {
|
||||
/* This is the inner loop */
|
||||
sum += *source++;
|
||||
count -= 2;
|
||||
}
|
||||
|
||||
/* Add left-over byte, if any */
|
||||
if (count > 0) {
|
||||
/* Make sure that the left-over byte is added correctly both
|
||||
* with little and big endian hosts */
|
||||
uint16_t tmp = 0;
|
||||
*(uint8_t *) (&tmp) = * (uint8_t *) source;
|
||||
sum += tmp;
|
||||
}
|
||||
/* Fold 32-bit sum to 16 bits */
|
||||
while (sum >> 16)
|
||||
sum = (sum & 0xffff) + (sum >> 16);
|
||||
|
||||
return ~sum;
|
||||
}
|
||||
|
||||
|
||||
/* Construct a ip/udp header for a packet, and specify the source and dest hardware address */
|
||||
int raw_packet(struct dhcpMessage *payload, uint32_t source_ip, int source_port,
|
||||
uint32_t dest_ip, int dest_port, uint8_t *dest_arp, int ifindex)
|
||||
{
|
||||
int fd;
|
||||
int result;
|
||||
struct sockaddr_ll dest;
|
||||
struct udp_dhcp_packet packet;
|
||||
|
||||
if ((fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) {
|
||||
DEBUG(LOG_ERR, "socket call failed: %m");
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&dest, 0, sizeof(dest));
|
||||
memset(&packet, 0, sizeof(packet));
|
||||
|
||||
dest.sll_family = AF_PACKET;
|
||||
dest.sll_protocol = htons(ETH_P_IP);
|
||||
dest.sll_ifindex = ifindex;
|
||||
dest.sll_halen = 6;
|
||||
memcpy(dest.sll_addr, dest_arp, 6);
|
||||
if (bind(fd, (struct sockaddr *)&dest, sizeof(struct sockaddr_ll)) < 0) {
|
||||
DEBUG(LOG_ERR, "bind call failed: %m");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
packet.ip.protocol = IPPROTO_UDP;
|
||||
packet.ip.saddr = source_ip;
|
||||
packet.ip.daddr = dest_ip;
|
||||
packet.udp.source = htons(source_port);
|
||||
packet.udp.dest = htons(dest_port);
|
||||
packet.udp.len = htons(sizeof(packet.udp) + sizeof(struct dhcpMessage)); /* cheat on the psuedo-header */
|
||||
packet.ip.tot_len = packet.udp.len;
|
||||
memcpy(&(packet.data), payload, sizeof(struct dhcpMessage));
|
||||
packet.udp.check = checksum(&packet, sizeof(struct udp_dhcp_packet));
|
||||
|
||||
packet.ip.tot_len = htons(sizeof(struct udp_dhcp_packet));
|
||||
packet.ip.ihl = sizeof(packet.ip) >> 2;
|
||||
packet.ip.version = IPVERSION;
|
||||
packet.ip.ttl = IPDEFTTL;
|
||||
packet.ip.check = checksum(&(packet.ip), sizeof(packet.ip));
|
||||
|
||||
result = sendto(fd, &packet, sizeof(struct udp_dhcp_packet), 0, (struct sockaddr *) &dest, sizeof(dest));
|
||||
if (result <= 0) {
|
||||
DEBUG(LOG_ERR, "write on socket failed: %m");
|
||||
}
|
||||
close(fd);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/* Let the kernel do all the work for packet generation */
|
||||
int kernel_packet(struct dhcpMessage *payload, uint32_t source_ip, int source_port,
|
||||
uint32_t dest_ip, int dest_port)
|
||||
{
|
||||
int n = 1;
|
||||
int fd, result;
|
||||
struct sockaddr_in client;
|
||||
|
||||
if ((fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
|
||||
return -1;
|
||||
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &n, sizeof(n)) == -1)
|
||||
{
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&client, 0, sizeof(client));
|
||||
client.sin_family = AF_INET;
|
||||
client.sin_port = htons(source_port);
|
||||
client.sin_addr.s_addr = source_ip;
|
||||
|
||||
if (bind(fd, (struct sockaddr *)&client, sizeof(struct sockaddr)) == -1)
|
||||
{
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&client, 0, sizeof(client));
|
||||
client.sin_family = AF_INET;
|
||||
client.sin_port = htons(dest_port);
|
||||
client.sin_addr.s_addr = dest_ip;
|
||||
|
||||
if (connect(fd, (struct sockaddr *)&client, sizeof(struct sockaddr)) == -1)
|
||||
{
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
result = write(fd, payload, sizeof(struct dhcpMessage));
|
||||
close(fd);
|
||||
return result;
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
#ifndef _PACKET_H
|
||||
#define _PACKET_H
|
||||
|
||||
#include <netinet/udp.h>
|
||||
#include <netinet/ip.h>
|
||||
|
||||
struct dhcpMessage {
|
||||
uint8_t op;
|
||||
uint8_t htype;
|
||||
uint8_t hlen;
|
||||
uint8_t hops;
|
||||
uint32_t xid;
|
||||
uint16_t secs;
|
||||
uint16_t flags;
|
||||
uint32_t ciaddr;
|
||||
uint32_t yiaddr;
|
||||
uint32_t siaddr;
|
||||
uint32_t giaddr;
|
||||
uint8_t chaddr[16];
|
||||
uint8_t sname[64];
|
||||
uint8_t file[128];
|
||||
uint32_t cookie;
|
||||
uint8_t options[308]; /* 312 - cookie */
|
||||
};
|
||||
|
||||
struct udp_dhcp_packet {
|
||||
struct iphdr ip;
|
||||
struct udphdr udp;
|
||||
struct dhcpMessage data;
|
||||
};
|
||||
|
||||
void init_header(struct dhcpMessage *packet, char type);
|
||||
int get_packet(struct dhcpMessage *packet, int fd);
|
||||
uint16_t checksum(void *addr, int count);
|
||||
int raw_packet(struct dhcpMessage *payload, uint32_t source_ip, int source_port,
|
||||
uint32_t dest_ip, int dest_port, uint8_t *dest_arp, int ifindex);
|
||||
int kernel_packet(struct dhcpMessage *payload, uint32_t source_ip, int source_port,
|
||||
uint32_t dest_ip, int dest_port);
|
||||
|
||||
|
||||
#endif
|
||||
@@ -1,75 +0,0 @@
|
||||
/* pidfile.c
|
||||
*
|
||||
* Functions to assist in the writing and removing of pidfiles.
|
||||
*
|
||||
* Russ Dill <Russ.Dill@asu.edu> September 2001
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "pidfile.h"
|
||||
#include "common.h"
|
||||
|
||||
static char *saved_pidfile;
|
||||
|
||||
static void pidfile_delete(void)
|
||||
{
|
||||
if (saved_pidfile) unlink(saved_pidfile);
|
||||
}
|
||||
|
||||
|
||||
int pidfile_acquire(const char *pidfile)
|
||||
{
|
||||
int pid_fd;
|
||||
if (!pidfile) return -1;
|
||||
|
||||
pid_fd = open(pidfile, O_CREAT | O_WRONLY, 0644);
|
||||
if (pid_fd < 0) {
|
||||
LOG(LOG_ERR, "Unable to open pidfile %s: %m\n", pidfile);
|
||||
} else {
|
||||
lockf(pid_fd, F_LOCK, 0);
|
||||
if (!saved_pidfile)
|
||||
atexit(pidfile_delete);
|
||||
saved_pidfile = (char *) pidfile;
|
||||
}
|
||||
|
||||
return pid_fd;
|
||||
}
|
||||
|
||||
|
||||
void pidfile_write_release(int pid_fd)
|
||||
{
|
||||
FILE *out;
|
||||
|
||||
if (pid_fd < 0) return;
|
||||
|
||||
if ((out = fdopen(pid_fd, "w")) != NULL) {
|
||||
fprintf(out, "%d\n", getpid());
|
||||
fclose(out);
|
||||
}
|
||||
lockf(pid_fd, F_UNLCK, 0);
|
||||
close(pid_fd);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
/* pidfile.h
|
||||
*
|
||||
* Functions to assist in the writing and removing of pidfiles.
|
||||
*
|
||||
* Russ Dill <Russ.Dill@asu.edu> September 2001
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
|
||||
int pidfile_acquire(const char *pidfile);
|
||||
void pidfile_write_release(int pid_fd);
|
||||
|
||||
@@ -1,242 +0,0 @@
|
||||
/* script.c
|
||||
*
|
||||
* Functions to call the DHCP client notification scripts
|
||||
*
|
||||
* Russ Dill <Russ.Dill@asu.edu> July 2001
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "options.h"
|
||||
#include "dhcpd.h"
|
||||
#include "dhcpc.h"
|
||||
#include "common.h"
|
||||
|
||||
/* get a rough idea of how long an option will be (rounding up...) */
|
||||
static const int max_option_length[] = {
|
||||
[OPTION_IP] = sizeof("255.255.255.255 "),
|
||||
[OPTION_IP_PAIR] = sizeof("255.255.255.255 ") * 2,
|
||||
[OPTION_STRING] = 1,
|
||||
[OPTION_BOOLEAN] = sizeof("yes "),
|
||||
[OPTION_U8] = sizeof("255 "),
|
||||
[OPTION_U16] = sizeof("65535 "),
|
||||
[OPTION_S16] = sizeof("-32768 "),
|
||||
[OPTION_U32] = sizeof("4294967295 "),
|
||||
[OPTION_S32] = sizeof("-2147483684 "),
|
||||
};
|
||||
|
||||
|
||||
static inline int upper_length(int length, int opt_index)
|
||||
{
|
||||
return max_option_length[opt_index] *
|
||||
(length / option_lengths[opt_index]);
|
||||
}
|
||||
|
||||
|
||||
static int sprintip(char *dest, char *pre, uint8_t *ip)
|
||||
{
|
||||
return sprintf(dest, "%s%d.%d.%d.%d", pre, ip[0], ip[1], ip[2], ip[3]);
|
||||
}
|
||||
|
||||
|
||||
/* really simple implementation, just count the bits */
|
||||
static int mton(struct in_addr *mask)
|
||||
{
|
||||
int i;
|
||||
unsigned long bits = ntohl(mask->s_addr);
|
||||
/* too bad one can't check the carry bit, etc in c bit
|
||||
* shifting */
|
||||
for (i = 0; i < 32 && !((bits >> i) & 1); i++);
|
||||
return 32 - i;
|
||||
}
|
||||
|
||||
|
||||
/* Fill dest with the text of option 'option'. */
|
||||
static void fill_options(char *dest, uint8_t *option, struct dhcp_option *type_p)
|
||||
{
|
||||
int type, optlen;
|
||||
uint16_t val_u16;
|
||||
int16_t val_s16;
|
||||
uint32_t val_u32;
|
||||
int32_t val_s32;
|
||||
int len = option[OPT_LEN - 2];
|
||||
|
||||
dest += sprintf(dest, "%s=", type_p->name);
|
||||
|
||||
type = type_p->flags & TYPE_MASK;
|
||||
optlen = option_lengths[type];
|
||||
for(;;) {
|
||||
switch (type) {
|
||||
case OPTION_IP_PAIR:
|
||||
dest += sprintip(dest, "", option);
|
||||
*(dest++) = '/';
|
||||
option += 4;
|
||||
optlen = 4;
|
||||
case OPTION_IP: /* Works regardless of host byte order. */
|
||||
dest += sprintip(dest, "", option);
|
||||
break;
|
||||
case OPTION_BOOLEAN:
|
||||
dest += sprintf(dest, *option ? "yes" : "no");
|
||||
break;
|
||||
case OPTION_U8:
|
||||
dest += sprintf(dest, "%u", *option);
|
||||
break;
|
||||
case OPTION_U16:
|
||||
memcpy(&val_u16, option, 2);
|
||||
dest += sprintf(dest, "%u", ntohs(val_u16));
|
||||
break;
|
||||
case OPTION_S16:
|
||||
memcpy(&val_s16, option, 2);
|
||||
dest += sprintf(dest, "%d", ntohs(val_s16));
|
||||
break;
|
||||
case OPTION_U32:
|
||||
memcpy(&val_u32, option, 4);
|
||||
dest += sprintf(dest, "%lu", (unsigned long) ntohl(val_u32));
|
||||
break;
|
||||
case OPTION_S32:
|
||||
memcpy(&val_s32, option, 4);
|
||||
dest += sprintf(dest, "%ld", (long) ntohl(val_s32));
|
||||
break;
|
||||
case OPTION_STRING:
|
||||
memcpy(dest, option, len);
|
||||
dest[len] = '\0';
|
||||
return; /* Short circuit this case */
|
||||
}
|
||||
option += optlen;
|
||||
len -= optlen;
|
||||
if (len <= 0) break;
|
||||
dest += sprintf(dest, " ");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* put all the parameters into an environment */
|
||||
static char **fill_envp(struct dhcpMessage *packet)
|
||||
{
|
||||
int num_options = 0;
|
||||
int i, j;
|
||||
char **envp;
|
||||
uint8_t *temp;
|
||||
struct in_addr subnet;
|
||||
char over = 0;
|
||||
|
||||
if (packet == NULL)
|
||||
num_options = 0;
|
||||
else {
|
||||
for (i = 0; dhcp_options[i].code; i++)
|
||||
if (get_option(packet, dhcp_options[i].code)) {
|
||||
num_options++;
|
||||
if (dhcp_options[i].code == DHCP_SUBNET)
|
||||
num_options++; /* for mton */
|
||||
}
|
||||
if (packet->siaddr) num_options++;
|
||||
if ((temp = get_option(packet, DHCP_OPTION_OVER)))
|
||||
over = *temp;
|
||||
if (!(over & FILE_FIELD) && packet->file[0]) num_options++;
|
||||
if (!(over & SNAME_FIELD) && packet->sname[0]) num_options++;
|
||||
}
|
||||
|
||||
envp = xcalloc(sizeof(char *), num_options + 5);
|
||||
j = 0;
|
||||
asprintf(&envp[j++], "interface=%s", client_config.interface);
|
||||
asprintf(&envp[j++], "%s=%s", "PATH",
|
||||
getenv("PATH") ? : "/bin:/usr/bin:/sbin:/usr/sbin");
|
||||
asprintf(&envp[j++], "%s=%s", "HOME", getenv("HOME") ? : "/");
|
||||
|
||||
if (packet == NULL) return envp;
|
||||
|
||||
envp[j] = xmalloc(sizeof("ip=255.255.255.255"));
|
||||
sprintip(envp[j++], "ip=", (uint8_t *) &packet->yiaddr);
|
||||
|
||||
|
||||
for (i = 0; dhcp_options[i].code; i++) {
|
||||
if (!(temp = get_option(packet, dhcp_options[i].code)))
|
||||
continue;
|
||||
envp[j] = xmalloc(upper_length(temp[OPT_LEN - 2],
|
||||
dhcp_options[i].flags & TYPE_MASK) + strlen(dhcp_options[i].name) + 2);
|
||||
fill_options(envp[j++], temp, &dhcp_options[i]);
|
||||
|
||||
/* Fill in a subnet bits option for things like /24 */
|
||||
if (dhcp_options[i].code == DHCP_SUBNET) {
|
||||
memcpy(&subnet, temp, 4);
|
||||
asprintf(&envp[j++], "mask=%d", mton(&subnet));
|
||||
}
|
||||
}
|
||||
if (packet->siaddr) {
|
||||
envp[j] = xmalloc(sizeof("siaddr=255.255.255.255"));
|
||||
sprintip(envp[j++], "siaddr=", (uint8_t *) &packet->siaddr);
|
||||
}
|
||||
if (!(over & FILE_FIELD) && packet->file[0]) {
|
||||
/* watch out for invalid packets */
|
||||
packet->file[sizeof(packet->file) - 1] = '\0';
|
||||
asprintf(&envp[j++], "boot_file=%s", packet->file);
|
||||
}
|
||||
if (!(over & SNAME_FIELD) && packet->sname[0]) {
|
||||
/* watch out for invalid packets */
|
||||
packet->sname[sizeof(packet->sname) - 1] = '\0';
|
||||
asprintf(&envp[j++], "sname=%s", packet->sname);
|
||||
}
|
||||
return envp;
|
||||
}
|
||||
|
||||
|
||||
/* Call a script with a par file and env vars */
|
||||
void run_script(struct dhcpMessage *packet, const char *name)
|
||||
{
|
||||
int pid;
|
||||
char **envp, **curr;
|
||||
|
||||
if (client_config.callback == NULL)
|
||||
return;
|
||||
|
||||
DEBUG(LOG_INFO, "callbacking'ing %p", client_config.callback);
|
||||
|
||||
envp = fill_envp(packet);
|
||||
/* call script */
|
||||
// pid = vfork();
|
||||
// if (pid) {
|
||||
// waitpid(pid, NULL, 0);
|
||||
// for (curr = envp; *curr; curr++) free(*curr);
|
||||
// free(envp);
|
||||
// return;
|
||||
// } else if (pid == 0) {
|
||||
// /* close fd's? */
|
||||
|
||||
// /* exec script */
|
||||
// execle(client_config.script, client_config.script,
|
||||
// name, NULL, envp);
|
||||
// LOG(LOG_ERR, "script %s failed: %m", client_config.script);
|
||||
// exit(1);
|
||||
// }
|
||||
client_config.callback(name, envp, client_config.callback_data);
|
||||
|
||||
// Free strings
|
||||
char **e = envp;
|
||||
while (*e) {
|
||||
free(*e);
|
||||
e++;
|
||||
}
|
||||
free(envp);
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
#ifndef _SCRIPT_H
|
||||
#define _SCRIPT_H
|
||||
|
||||
void run_script(struct dhcpMessage *packet, const char *name);
|
||||
|
||||
#endif
|
||||
@@ -1,78 +0,0 @@
|
||||
/* signalpipe.c
|
||||
*
|
||||
* Signal pipe infrastructure. A reliable way of delivering signals.
|
||||
*
|
||||
* Russ Dill <Russ.Dill@asu.edu> December 2003
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/select.h>
|
||||
|
||||
|
||||
#include "signalpipe.h"
|
||||
#include "common.h"
|
||||
|
||||
static int signal_pipe[2];
|
||||
|
||||
static void signal_handler(int sig)
|
||||
{
|
||||
if (send(signal_pipe[1], &sig, sizeof(sig), MSG_DONTWAIT) < 0)
|
||||
DEBUG(LOG_ERR, "Could not send signal: %m");
|
||||
}
|
||||
|
||||
|
||||
/* Call this before doing anything else. Sets up the socket pair
|
||||
* and installs the signal handler */
|
||||
void udhcp_sp_setup(void)
|
||||
{
|
||||
socketpair(AF_UNIX, SOCK_STREAM, 0, signal_pipe);
|
||||
signal(SIGUSR1, signal_handler);
|
||||
signal(SIGUSR2, signal_handler);
|
||||
signal(SIGTERM, signal_handler);
|
||||
}
|
||||
|
||||
|
||||
/* Quick little function to setup the rfds. Will return the
|
||||
* max_fd for use with select. Limited in that you can only pass
|
||||
* one extra fd */
|
||||
int udhcp_sp_fd_set(fd_set *rfds, int extra_fd)
|
||||
{
|
||||
FD_ZERO(rfds);
|
||||
FD_SET(signal_pipe[0], rfds);
|
||||
if (extra_fd >= 0) FD_SET(extra_fd, rfds);
|
||||
return signal_pipe[0] > extra_fd ? signal_pipe[0] : extra_fd;
|
||||
}
|
||||
|
||||
|
||||
/* Read a signal from the signal pipe. Returns 0 if there is
|
||||
* no signal, -1 on error (and sets errno appropriately), and
|
||||
* your signal on success */
|
||||
int udhcp_sp_read(fd_set *rfds)
|
||||
{
|
||||
int sig;
|
||||
|
||||
if (!FD_ISSET(signal_pipe[0], rfds))
|
||||
return 0;
|
||||
|
||||
if (read(signal_pipe[0], &sig, sizeof(sig)) < 0)
|
||||
return -1;
|
||||
|
||||
return sig;
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
/* signalpipe.h
|
||||
*
|
||||
* Russ Dill <Russ.Dill@asu.edu> December 2003
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
void udhcp_sp_setup(void);
|
||||
int udhcp_sp_fd_set(fd_set *rfds, int extra_fd);
|
||||
int udhcp_sp_read(fd_set *rfds);
|
||||
@@ -1,137 +0,0 @@
|
||||
/*
|
||||
* socket.c -- DHCP server client/server socket creation
|
||||
*
|
||||
* udhcp client/server
|
||||
* Copyright (C) 1999 Matthew Ramsay <matthewr@moreton.com.au>
|
||||
* Chris Trew <ctrew@moreton.com.au>
|
||||
*
|
||||
* Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <netinet/in.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <net/if.h>
|
||||
#include <errno.h>
|
||||
#include <features.h>
|
||||
#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1
|
||||
#include <netpacket/packet.h>
|
||||
#include <net/ethernet.h>
|
||||
#else
|
||||
#include <asm/types.h>
|
||||
#include <linux/if_packet.h>
|
||||
#include <linux/if_ether.h>
|
||||
#endif
|
||||
|
||||
#include "socket.h"
|
||||
#include "common.h"
|
||||
|
||||
int read_interface(char *interface, int *ifindex, uint32_t *addr, uint8_t *arp)
|
||||
{
|
||||
int fd;
|
||||
struct ifreq ifr;
|
||||
struct sockaddr_in *our_ip;
|
||||
|
||||
memset(&ifr, 0, sizeof(struct ifreq));
|
||||
if((fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) >= 0) {
|
||||
ifr.ifr_addr.sa_family = AF_INET;
|
||||
//closes http://bugs.debian.org/283582
|
||||
//strcpy(ifr.ifr_name, interface);
|
||||
strncpy(ifr.ifr_name, interface, IFNAMSIZ-1);
|
||||
|
||||
if (addr) {
|
||||
if (ioctl(fd, SIOCGIFADDR, &ifr) == 0) {
|
||||
our_ip = (struct sockaddr_in *) &ifr.ifr_addr;
|
||||
*addr = our_ip->sin_addr.s_addr;
|
||||
DEBUG(LOG_INFO, "%s (our ip) = %s", ifr.ifr_name, inet_ntoa(our_ip->sin_addr));
|
||||
} else {
|
||||
LOG(LOG_ERR, "SIOCGIFADDR failed, is the interface up and configured?: %m");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (ioctl(fd, SIOCGIFINDEX, &ifr) == 0) {
|
||||
DEBUG(LOG_INFO, "adapter index %d", ifr.ifr_ifindex);
|
||||
*ifindex = ifr.ifr_ifindex;
|
||||
} else {
|
||||
LOG(LOG_ERR, "SIOCGIFINDEX failed!: %m");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
if (ioctl(fd, SIOCGIFHWADDR, &ifr) == 0) {
|
||||
memcpy(arp, ifr.ifr_hwaddr.sa_data, 6);
|
||||
DEBUG(LOG_INFO, "adapter hardware address %02x:%02x:%02x:%02x:%02x:%02x",
|
||||
arp[0], arp[1], arp[2], arp[3], arp[4], arp[5]);
|
||||
} else {
|
||||
LOG(LOG_ERR, "SIOCGIFHWADDR failed!: %m");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
LOG(LOG_ERR, "socket failed!: %m");
|
||||
return -1;
|
||||
}
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int listen_socket(uint32_t ip, int port, char *inf)
|
||||
{
|
||||
struct ifreq interface;
|
||||
int fd;
|
||||
struct sockaddr_in addr;
|
||||
int n = 1;
|
||||
|
||||
DEBUG(LOG_INFO, "Opening listen socket on 0x%08x:%d %s", ip, port, inf);
|
||||
if ((fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
|
||||
DEBUG(LOG_ERR, "socket call failed: %m");
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
addr.sin_addr.s_addr = ip;
|
||||
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &n, sizeof(n)) == -1) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char *) &n, sizeof(n)) == -1) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
strncpy(interface.ifr_ifrn.ifrn_name, inf, IFNAMSIZ);
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,(char *)&interface, sizeof(interface)) < 0) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (bind(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr)) == -1) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
/* socket.h */
|
||||
#ifndef _SOCKET_H
|
||||
#define _SOCKET_H
|
||||
|
||||
int read_interface(char *interface, int *ifindex, uint32_t *addr, uint8_t *arp);
|
||||
int listen_socket(uint32_t ip, int port, char *inf);
|
||||
|
||||
#endif
|
||||
@@ -1,6 +0,0 @@
|
||||
#ifndef _UDHCP_VERSION_H
|
||||
#define _UDHCP_VERSION_H
|
||||
|
||||
#define VERSION "0.9.9-pre"
|
||||
|
||||
#endif
|
||||
+427
-204
File diff suppressed because it is too large.
Load diff
+4
-1
@@ -7,7 +7,8 @@
|
||||
|
||||
struct wpa_ctrl;
|
||||
|
||||
void wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd, char *buf, size_t *buf_len);
|
||||
int wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd, char *buf,
|
||||
size_t *buf_len);
|
||||
int start_wpa_supplicant(const char *wireless_interface, const char *config_file, pid_t *pid);
|
||||
|
||||
int is_networkmanager_managing_device(const char *wireless_interface, int *is_managed);
|
||||
@@ -15,6 +16,8 @@ int disable_networkmanager_on_device(const char *wireless_interface);
|
||||
int enable_networkmanager_on_device(const char *wireless_interface);
|
||||
|
||||
void nlprint(const char *fmt, ...);
|
||||
ssize_t run_process_and_read_stdout(const char **args, char *read_buffer,
|
||||
size_t read_buffer_len);
|
||||
|
||||
void pipe_listen(int local, const char *wireless_interface, const char *log_file);
|
||||
|
||||
|
||||
Reference in new issue
Block a user