182 lines
6.7 KiB
C
182 lines
6.7 KiB
C
// MBMNET-FTP-Modul: läuft als eigener Hintergrund-Prozess, auch wenn die MBMNET-App geschlossen ist.
|
|
//
|
|
// Die App legt dieses Modul als /luma/sysmodules/0004013000F1E002.cxi ab und startet es über den
|
|
// Prozess-Manager (Luma lädt Systemmodule mit dieser Kennung von der SD). "FTP aus" in der App
|
|
// beendet den Prozess. Nach einem Neustart des 3DS ist es aus, bis die App es wieder startet.
|
|
//
|
|
// Die aktuelle Adresse steht für die App in /3ds/mbmnet/ftp-adresse.txt.
|
|
//
|
|
// Beenden immer sauber durch das Modul selbst (nie von außen abschießen - das brachte den 3DS zum
|
|
// Absturz): "FTP aus" legt /3ds/mbmnet/ftp-stopp an, beim Neustart/Ausschalten schickt das System
|
|
// die Benachrichtigung 0x100. Dann Verbindungen und Netzwerk-Dienst schließen und zurückkehren.
|
|
#include <3ds.h>
|
|
#include <arpa/inet.h>
|
|
#include <malloc.h>
|
|
#include <netinet/in.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/stat.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
|
|
#include "ftp.h"
|
|
#include "ftpstatus.h"
|
|
|
|
// Statusblock für die App (Adresse fest, siehe Makefile -> ftpmodul.sym)
|
|
volatile FtpStatus ftpStatus __attribute__((used, aligned(4))) = { .magic = FTP_STATUS_MAGIC };
|
|
|
|
#define SOC_BUFFER_SIZE 0x20000
|
|
#define ADDRESS_FILE "sdmc:/3ds/mbmnet/ftp-adresse.txt"
|
|
#define LOG_FILE "sdmc:/3ds/mbmnet/ftp.log"
|
|
#define STOP_FILE "sdmc:/3ds/mbmnet/ftp-stopp"
|
|
|
|
static Handle notificationSemaphore;
|
|
static bool quit;
|
|
|
|
|
|
// Protokoll für die Fehlersuche (die App zeigt die letzte Zeile, wenn der Server nicht antwortet)
|
|
static void logf_(const char *fmt, ...)
|
|
{
|
|
FILE *f = fopen(LOG_FILE, "a");
|
|
if (!f) return;
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
vfprintf(f, fmt, ap);
|
|
va_end(ap);
|
|
fputc('\n', f);
|
|
fclose(f);
|
|
}
|
|
|
|
// Ein Hintergrund-Modul bekommt nur wenig Speicher: Heap fest vorgeben statt "alles, was frei ist"
|
|
u32 __ctru_heap_size = 0x80000;
|
|
u32 __ctru_linear_heap_size = 0x1000;
|
|
|
|
// Nur die Dienste, die das Modul braucht (kein Grafik-/HOME-Menü-Dienst wie bei einer App)
|
|
void __appInit(void)
|
|
{
|
|
// srv ist beim Start evtl. noch belegt: kurz wiederholen
|
|
for (int i = 0; i < 100 && R_FAILED(ftpStatus.srv = srvInit()); i++) svcSleepThread(50 * 1000 * 1000);
|
|
ftpStatus.fs = fsInit();
|
|
ftpStatus.sdmc = archiveMountSdmc();
|
|
ftpStatus.stage = FTP_STAGE_INIT;
|
|
}
|
|
|
|
void __appExit(void)
|
|
{
|
|
archiveUnmountAll();
|
|
fsExit();
|
|
srvExit();
|
|
}
|
|
|
|
bool ftp_should_exit(void)
|
|
{
|
|
ftpStatus.heartbeat++;
|
|
if (ftpStatus.stop) quit = true; // Stopp-Bit der App (geht auch ohne SD)
|
|
if (quit) return true;
|
|
// Beenden-Aufforderung des Systems (Neustart, Ausschalten)
|
|
if (notificationSemaphore && R_SUCCEEDED(svcWaitSynchronization(notificationSemaphore, 0))) {
|
|
u32 id = 0;
|
|
if (R_SUCCEEDED(srvReceiveNotification(&id)) && id == 0x100) quit = true;
|
|
}
|
|
// Stopp-Datei der App, höchstens alle 2 Sekunden nachsehen
|
|
static u64 lastCheck;
|
|
u64 now = osGetTime();
|
|
if (!quit && now - lastCheck >= 2000) {
|
|
lastCheck = now;
|
|
struct stat st;
|
|
if (stat(STOP_FILE, &st) == 0) quit = true;
|
|
}
|
|
return quit;
|
|
}
|
|
|
|
// Wartet ms Millisekunden, bricht aber ab, sobald das Modul aufhören soll
|
|
static void sleep_ms(int ms)
|
|
{
|
|
for (int waited = 0; waited < ms && !ftp_should_exit(); waited += 100) svcSleepThread(100LL * 1000 * 1000);
|
|
}
|
|
|
|
static void write_address(u32 ip)
|
|
{
|
|
mkdir("sdmc:/3ds", 0777);
|
|
mkdir("sdmc:/3ds/mbmnet", 0777);
|
|
FILE *f = fopen(ADDRESS_FILE, "w");
|
|
if (!f) return;
|
|
if (ip) fprintf(f, "%lu.%lu.%lu.%lu:%d\n", (unsigned long)(ip & 0xFF), (unsigned long)((ip >> 8) & 0xFF),
|
|
(unsigned long)((ip >> 16) & 0xFF), (unsigned long)(ip >> 24), CONTROL_PORT);
|
|
fclose(f);
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
remove(LOG_FILE);
|
|
remove(STOP_FILE);
|
|
srvEnableNotification(¬ificationSemaphore); // für die Beenden-Aufforderung 0x100
|
|
logf_("MBMNET-FTP-Modul gestartet: srv %08lX, fs %08lX, sdmc %08lX, frei (Base) %lld KB",
|
|
(unsigned long)ftpStatus.srv, (unsigned long)ftpStatus.fs, (unsigned long)ftpStatus.sdmc,
|
|
osGetMemRegionFree(MEMREGION_BASE) / 1024);
|
|
u32 *socBuffer = memalign(0x1000, SOC_BUFFER_SIZE);
|
|
if (!socBuffer) { logf_("Fehler: kein Speicher für den Netzwerk-Puffer"); return 1; }
|
|
|
|
while (!ftp_should_exit()) {
|
|
// Netzwerk-Dienst (nach dem Schlafmodus ggf. neu)
|
|
Result rc;
|
|
int tries = 0;
|
|
ftpStatus.stage = FTP_STAGE_SOC;
|
|
while (R_FAILED(rc = ftpStatus.soc = socInit(socBuffer, SOC_BUFFER_SIZE))) {
|
|
if (ftp_should_exit()) goto done;
|
|
if (tries++ % 15 == 0) logf_("Netzwerk-Dienst (socInit) fehlgeschlagen: %08lX", (unsigned long)rc);
|
|
sleep_ms(2000);
|
|
}
|
|
logf_("Netzwerk-Dienst bereit");
|
|
|
|
int failures = 0, waits = 0;
|
|
while (failures < 5 && !ftp_should_exit()) {
|
|
u32 ip = (u32)gethostid();
|
|
ftpStatus.ip = ip;
|
|
if (!ip) { // noch kein WLAN
|
|
ftpStatus.stage = FTP_STAGE_WIFI;
|
|
if (waits++ % 15 == 0) logf_("Warte auf WLAN (keine IP)");
|
|
write_address(0);
|
|
sleep_ms(2000);
|
|
continue;
|
|
}
|
|
waits = 0;
|
|
|
|
int fd = socket(AF_INET, SOCK_STREAM, 0);
|
|
struct sockaddr_in addr = { .sin_family = AF_INET, .sin_port = htons(CONTROL_PORT), .sin_addr.s_addr = INADDR_ANY };
|
|
if (fd < 0 || bind(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0 || listen(fd, 2) != 0) {
|
|
logf_("Port %d öffnen fehlgeschlagen (Socket %d, errno %d)", CONTROL_PORT, fd, errno);
|
|
ftpStatus.stage = FTP_STAGE_PORT;
|
|
ftpStatus.bindErrno = errno;
|
|
if (fd >= 0) close(fd);
|
|
failures++;
|
|
sleep_ms(2000);
|
|
continue;
|
|
}
|
|
failures = 0;
|
|
ftpStatus.stage = FTP_STAGE_READY;
|
|
write_address(ip);
|
|
logf_("Bereit: ftp://%lu.%lu.%lu.%lu:%d", (unsigned long)(ip & 0xFF), (unsigned long)((ip >> 8) & 0xFF),
|
|
(unsigned long)((ip >> 16) & 0xFF), (unsigned long)(ip >> 24), CONTROL_PORT);
|
|
ftp_serve(fd, ip); // kehrt zurück, wenn das Netz weg ist oder sich die IP ändert
|
|
close(fd);
|
|
sleep_ms(500);
|
|
}
|
|
socExit();
|
|
if (ftp_should_exit()) break;
|
|
// Mehrfach kein Socket möglich (z. B. nach dem Schlafmodus): Netzwerk-Dienst neu aufsetzen
|
|
logf_("Netzwerk-Dienst wird neu aufgesetzt");
|
|
sleep_ms(2000);
|
|
}
|
|
done:
|
|
ftpStatus.stage = FTP_STAGE_EXIT;
|
|
// Sauber beenden: Adresse weg, Stopp-Datei weg; libctru schließt danach fs und srv
|
|
remove(ADDRESS_FILE);
|
|
remove(STOP_FILE);
|
|
logf_("Beendet");
|
|
free(socBuffer);
|
|
return 0;
|
|
}
|