mirror of
https://github.com/holdmysocks/ps5-tailscale.git
synced 2026-10-06 14:00:18 +02:00
A payload that runs the Tailscale client (tsnet, userspace networking) on a jailbroken PS5: a C launcher built with ps5-payload-sdk that loads a Go program in-process, an installer that registers it with the console's payload autoloader and adds a home screen icon, and the patch that makes Go 1.27.1 speak the PS5's FreeBSD 9 era syscall interface.
81 lines
1.8 KiB
Go
81 lines
1.8 KiB
Go
//go:build freebsd
|
|
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"sync"
|
|
"syscall"
|
|
)
|
|
|
|
// console writes to whoever sent the payload. The ELF loader hands a payload
|
|
// its TCP connection as stdin/stdout/stderr, and that connection goes away as
|
|
// soon as the sender disconnects.
|
|
//
|
|
// Go kills the process when a write to fd 1 or 2 fails with EPIPE, so the
|
|
// connection is moved to another descriptor and the first failed write turns
|
|
// the console off for good.
|
|
type console struct {
|
|
mu sync.Mutex
|
|
fd int
|
|
}
|
|
|
|
func newConsole() *console {
|
|
fd, err := syscall.Dup(1)
|
|
if err != nil {
|
|
return &console{fd: -1}
|
|
}
|
|
syscall.CloseOnExec(fd)
|
|
return &console{fd: fd}
|
|
}
|
|
|
|
func (c *console) Printf(format string, args ...any) {
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
if c.fd < 0 {
|
|
return
|
|
}
|
|
b := []byte(fmt.Sprintf(format, args...))
|
|
for len(b) > 0 {
|
|
n, err := syscall.Write(c.fd, b)
|
|
if err != nil || n <= 0 {
|
|
syscall.Close(c.fd)
|
|
c.fd = -1
|
|
return
|
|
}
|
|
b = b[n:]
|
|
}
|
|
}
|
|
|
|
// redirectStdio points stdout and stderr at the log file.
|
|
func redirectStdio(f *os.File) {
|
|
syscall.Dup2(int(f.Fd()), 1)
|
|
syscall.Dup2(int(f.Fd()), 2)
|
|
}
|
|
|
|
// notify shows a pop-up on the PS5 screen.
|
|
//
|
|
// It does what libkernel's sceKernelSendNotificationRequest does: write a
|
|
// fixed-size request to the notification device. The request is 0xC30 bytes
|
|
// with the UTF-8 message at offset 0x2D.
|
|
func notify(format string, args ...any) {
|
|
const (
|
|
requestSize = 0xC30
|
|
messageOffset = 0x2D
|
|
)
|
|
var req [requestSize]byte
|
|
msg := fmt.Sprintf(format, args...)
|
|
if max := requestSize - messageOffset - 1; len(msg) > max {
|
|
msg = msg[:max]
|
|
}
|
|
copy(req[messageOffset:], msg)
|
|
|
|
fd, err := syscall.Open("/dev/notification0", syscall.O_WRONLY|syscall.O_NONBLOCK, 0)
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer syscall.Close(fd)
|
|
syscall.Write(fd, req[:])
|
|
}
|