Files
mortyzip--KeepAwake/src/ps5.c
T

329 lines
6.9 KiB
C

/* KeepAwake - keep the PS5 out of rest mode while this payload is running.
The idle timer is reset by calling sceSystemServicePowerTick() every few
seconds. A control page is served on TCP port 9031 (see web.h). Holding
that port also means only one instance runs, and a second copy of the
payload asks the running one to quit. */
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <ifaddrs.h>
#include <net/if.h>
#include <netinet/in.h>
#include <pthread.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <time.h>
#include <unistd.h>
#ifndef KEEPAWAKE_VERSION
#define KEEPAWAKE_VERSION "dev"
#endif
typedef struct notify_request {
char useless1[45];
char message[3075];
} notify_request_t;
int sceKernelSendNotificationRequest(int, notify_request_t*, size_t, int);
int sceSystemServicePowerTick(void);
int sceSystemServiceLaunchWebBrowser(const char *uri, void *param);
int sceUserServiceInitialize(void *param);
static long long g_started_ms;
static long long
now_ms(void) {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (long long)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
}
static void
notify(const char *fmt, ...) {
notify_request_t req;
va_list args;
bzero(&req, sizeof req);
va_start(args, fmt);
vsnprintf(req.message, sizeof req.message, fmt, args);
va_end(args);
printf("[KeepAwake] %s\n", req.message);
fflush(stdout);
sceKernelSendNotificationRequest(0, &req, sizeof req, 0);
}
#define WEB_CONSOLE "PS5"
#include "web.h"
static int
web_recv(int fd, void *buf, size_t len) {
ssize_t rc = recv(fd, buf, len, 0);
if(rc < 0) {
return (errno == EAGAIN || errno == EWOULDBLOCK) ? WEB_AGAIN : WEB_ERROR;
}
return rc;
}
static int
web_send(int fd, const void *buf, size_t len) {
ssize_t rc = send(fd, buf, len, 0);
if(rc < 0) {
return (errno == EAGAIN || errno == EWOULDBLOCK) ? WEB_AGAIN : WEB_ERROR;
}
return rc;
}
static void
web_sleep_ms(int ms) {
usleep(ms * 1000);
}
static long long
web_uptime_ms(void) {
return now_ms() - g_started_ms;
}
static long long
web_uptime(void) {
return web_uptime_ms() / 1000;
}
static int
web_net_bytes(unsigned long long *rx, unsigned long long *tx) {
struct ifaddrs *ifs;
struct ifaddrs *it;
struct if_data *data;
int found = 0;
*rx = *tx = 0;
if(getifaddrs(&ifs) != 0) {
return -1;
}
// The link-level (AF_LINK) entries carry each interface's counters.
for(it = ifs; it; it = it->ifa_next) {
if(!it->ifa_addr || it->ifa_addr->sa_family != AF_LINK || !it->ifa_data ||
(it->ifa_flags & IFF_LOOPBACK)) {
continue;
}
data = it->ifa_data;
*rx += data->ifi_ibytes;
*tx += data->ifi_obytes;
found = 1;
}
freeifaddrs(ifs);
return found ? 0 : -1;
}
static void
web_get_ip(char *buf, size_t size) {
struct ifaddrs *ifs;
struct ifaddrs *it;
buf[0] = 0;
if(getifaddrs(&ifs) != 0) {
return;
}
for(it = ifs; it; it = it->ifa_next) {
if(!it->ifa_addr || it->ifa_addr->sa_family != AF_INET ||
!(it->ifa_flags & IFF_UP) || (it->ifa_flags & IFF_LOOPBACK)) {
continue;
}
inet_ntop(AF_INET, &((struct sockaddr_in*)it->ifa_addr)->sin_addr,
buf, size);
break;
}
freeifaddrs(ifs);
}
static void
web_notify(const char *msg) {
notify("%s", msg);
}
/* On its own thread so a slow browser launch doesn't hold up the page. */
static void*
open_browser_thread(void *arg) {
char url[64];
snprintf(url, sizeof url, "http://127.0.0.1:%d/", KEEPAWAKE_PORT);
sceUserServiceInitialize(0);
if(sceSystemServiceLaunchWebBrowser(url, 0) != 0) {
notify("Keep Awake couldn't open the browser");
}
return NULL;
}
static void
open_browser(void) {
pthread_t thread;
if(pthread_create(&thread, NULL, open_browser_thread, NULL) == 0) {
pthread_detach(thread);
}
}
/* Ask an already running instance to quit. Returns 0 if it confirmed. */
static int
quit_running_instance(void) {
struct sockaddr_in addr;
struct timeval tv = {3, 0};
char resp[64];
ssize_t n;
int fd;
if((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
return -1;
}
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
bzero(&addr, sizeof addr);
addr.sin_family = AF_INET;
addr.sin_port = htons(KEEPAWAKE_PORT);
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
if(connect(fd, (struct sockaddr*)&addr, sizeof addr) != 0 ||
send(fd, web_quit_request, sizeof web_quit_request - 1, 0) < 0) {
close(fd);
return -1;
}
n = recv(fd, resp, sizeof resp - 1, 0);
close(fd);
if(n <= 0) {
return -1;
}
resp[n] = 0;
return strstr(resp, " 200 ") ? 0 : -1;
}
static int
open_control_socket(void) {
struct sockaddr_in addr;
int yes = 1;
int fd;
if((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
return -1;
}
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes);
bzero(&addr, sizeof addr);
addr.sin_family = AF_INET;
addr.sin_port = htons(KEEPAWAKE_PORT);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(fd, (struct sockaddr*)&addr, sizeof addr) != 0 ||
listen(fd, 8) != 0) {
int err = errno;
close(fd);
errno = err;
return -1;
}
return fd;
}
int
main(void) {
struct timeval tv;
char ip[32];
fd_set fds;
int srv;
int cli;
int rc;
signal(SIGPIPE, SIG_IGN);
if((srv = open_control_socket()) < 0) {
if(errno == EADDRINUSE) {
// Port taken: an instance is already running, so close it.
// The running instance shows the "closed" toast itself.
if(quit_running_instance() == 0) {
return 0;
}
notify("Keep Awake failed to start:\nport %d is in use", KEEPAWAKE_PORT);
return -1;
}
notify("Keep Awake failed to start: %s", strerror(errno));
return -1;
}
g_started_ms = now_ms();
ka_load_settings();
web_get_ip(ip, sizeof ip);
if(ip[0]) {
notify("Keep Awake " KEEPAWAKE_VERSION " enabled\n"
"Control it at http://%s:%d", ip, KEEPAWAKE_PORT);
} else {
notify("Keep Awake " KEEPAWAKE_VERSION " enabled\n"
"Control page on port %d", KEEPAWAKE_PORT);
}
if(ka_open_on_start) {
open_browser();
}
while(1) {
FD_ZERO(&fds);
FD_SET(srv, &fds);
tv.tv_sec = ka_tick();
tv.tv_usec = 0;
rc = select(srv + 1, &fds, NULL, NULL, &tv);
if(rc < 0) {
if(errno == EINTR) {
continue;
}
notify("Keep Awake stopped: %s", strerror(errno));
break;
}
if(rc > 0 && FD_ISSET(srv, &fds)) {
if((cli = accept(srv, NULL, NULL)) < 0) {
if(errno != EINTR && errno != ECONNABORTED) {
usleep(100 * 1000); // e.g. out of descriptors: don't spin
}
continue;
}
fcntl(cli, F_SETFL, fcntl(cli, F_GETFL) | O_NONBLOCK);
rc = web_handle_client(cli);
close(cli);
if(rc) {
notify("Keep Awake " KEEPAWAKE_VERSION " closed");
break;
}
}
}
close(srv);
return 0;
}