mirror of
https://github.com/knutwurst/patchdl.git
synced 2026-10-06 07:00:26 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ec94f0578b | ||
|
|
88368e3df3 |
No files matched your search
+20
-11
@@ -475,7 +475,10 @@ patchdl_install_status_json(char *out, size_t out_sz) {
|
|||||||
char tid[32];
|
char tid[32];
|
||||||
char method[32];
|
char method[32];
|
||||||
int start_rc;
|
int start_rc;
|
||||||
ai_install_status_t st;
|
/* ai_install_status_t carries a 2 KB safety pad; live on the heap so
|
||||||
|
a frequently-polled /api/installstatus doesn't keep committing pages
|
||||||
|
on every MHD worker's stack. */
|
||||||
|
ai_install_status_t *st = NULL;
|
||||||
char status[17], src_type[9];
|
char status[17], src_type[9];
|
||||||
int rc;
|
int rc;
|
||||||
int progress = 0;
|
int progress = 0;
|
||||||
@@ -514,20 +517,25 @@ patchdl_install_status_json(char *out, size_t out_sz) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
st = calloc(1, sizeof(*st));
|
||||||
|
if (!st) {
|
||||||
|
snprintf(out, out_sz, "{\"error\":\"oom\"}");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
/* sceAppInstUtilGetInstallStatus(char *content_id_out, status_t *status):
|
/* sceAppInstUtilGetInstallStatus(char *content_id_out, status_t *status):
|
||||||
first arg is an OUTPUT buffer that receives the current install's content_id.
|
first arg is an OUTPUT buffer that receives the current install's content_id.
|
||||||
Do NOT pass `cid` there — it would be overwritten. The visible id fits in
|
Do NOT pass `cid` there — it would be overwritten. The visible id fits in
|
||||||
0x30 bytes but Sony's NUL-pad length is unknown; use a padded buffer. */
|
0x30 bytes but Sony's NUL-pad length is unknown; use a padded buffer. */
|
||||||
{
|
{
|
||||||
char ai_cid_out[AI_CONTENTID_OUT_SIZE] = {0};
|
char ai_cid_out[AI_CONTENTID_OUT_SIZE] = {0};
|
||||||
memset(&st, 0, sizeof(st));
|
rc = ai_get_status(ai_cid_out, st);
|
||||||
rc = ai_get_status(ai_cid_out, &st);
|
|
||||||
(void)ai_cid_out; /* returned content_id for future use */
|
(void)ai_cid_out; /* returned content_id for future use */
|
||||||
}
|
}
|
||||||
copy_bounded(status, sizeof(status), st.status, sizeof(st.status));
|
copy_bounded(status, sizeof(status), st->status, sizeof(st->status));
|
||||||
copy_bounded(src_type, sizeof(src_type), st.src_type, sizeof(st.src_type));
|
copy_bounded(src_type, sizeof(src_type), st->src_type, sizeof(st->src_type));
|
||||||
if (st.total_size > 0)
|
if (st->total_size > 0)
|
||||||
progress = (int)((st.downloaded_size * 100) / st.total_size);
|
progress = (int)((st->downloaded_size * 100) / st->total_size);
|
||||||
if (progress < 0) progress = 0;
|
if (progress < 0) progress = 0;
|
||||||
if (progress > 100) progress = 100;
|
if (progress > 100) progress = 100;
|
||||||
terminal = (!strcmp(status, "playable") ||
|
terminal = (!strcmp(status, "playable") ||
|
||||||
@@ -542,10 +550,11 @@ patchdl_install_status_json(char *out, size_t out_sz) {
|
|||||||
"\"promote_progress\":%u,\"error_code\":%d}",
|
"\"promote_progress\":%u,\"error_code\":%d}",
|
||||||
terminal ? "true" : "false", cid, tid, method, start_rc, rc,
|
terminal ? "true" : "false", cid, tid, method, start_rc, rc,
|
||||||
status, src_type, progress,
|
status, src_type, progress,
|
||||||
(unsigned long long)st.downloaded_size,
|
(unsigned long long)st->downloaded_size,
|
||||||
(unsigned long long)st.total_size,
|
(unsigned long long)st->total_size,
|
||||||
(unsigned)st.promote_progress,
|
(unsigned)st->promote_progress,
|
||||||
(int)st.error_info.error_code);
|
(int)st->error_info.error_code);
|
||||||
|
free(st);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-3
@@ -31,9 +31,11 @@
|
|||||||
#define PATCHDL_MAX_TOTAL_BYTES (200ULL * 1024 * 1024 * 1024) /* 200 GiB */
|
#define PATCHDL_MAX_TOTAL_BYTES (200ULL * 1024 * 1024 * 1024) /* 200 GiB */
|
||||||
|
|
||||||
/* In-RAM buffer caps for full HTTP body fetches. version.xml is a few KB,
|
/* In-RAM buffer caps for full HTTP body fetches. version.xml is a few KB,
|
||||||
manifest JSON is a few MB at most — fail-closed beyond that. */
|
manifest JSON is a few MB at most — fail-closed beyond that. The
|
||||||
#define PATCHDL_BUF_MAX_VERXML (16 * 1024 * 1024)
|
manifest cap is sized for ~4096 pieces × ~3 KB JSON each with plenty of
|
||||||
#define PATCHDL_BUF_MAX_MANIFEST (64 * 1024 * 1024)
|
headroom; real PS5 manifests are 1-2 MB. */
|
||||||
|
#define PATCHDL_BUF_MAX_VERXML (4 * 1024 * 1024)
|
||||||
|
#define PATCHDL_BUF_MAX_MANIFEST (16 * 1024 * 1024)
|
||||||
|
|
||||||
#ifdef PATCHDL_HAVE_CURL
|
#ifdef PATCHDL_HAVE_CURL
|
||||||
/* Replacement for fopen("wb"/"r+b") that refuses to follow a symlink at the
|
/* Replacement for fopen("wb"/"r+b") that refuses to follow a symlink at the
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define PATCHDL_VERSION "0.0.5"
|
#define PATCHDL_VERSION "0.0.6"
|
||||||
@@ -183,10 +183,10 @@ patchdl_verxml_query(const char *url, uint32_t fw_bin, patchdl_verinfo_t *out) {
|
|||||||
if (!url || !out) return -1;
|
if (!url || !out) return -1;
|
||||||
memset(out, 0, sizeof(*out));
|
memset(out, 0, sizeof(*out));
|
||||||
|
|
||||||
/* version.xml is a few KB in practice; cap to 16 MiB so a misbehaving CDN
|
/* version.xml is a few KB in practice; cap so a misbehaving CDN can't
|
||||||
can't slurp unbounded RAM into the buffer. */
|
slurp unbounded RAM into the buffer. */
|
||||||
memset(&buf, 0, sizeof(buf));
|
memset(&buf, 0, sizeof(buf));
|
||||||
buf.max = 16 * 1024 * 1024;
|
buf.max = 4 * 1024 * 1024;
|
||||||
if (patchdl_http_get(url, &buf)) return -1;
|
if (patchdl_http_get(url, &buf)) return -1;
|
||||||
if (!buf.data || !buf.size) { free(buf.data); return -1; }
|
if (!buf.data || !buf.size) { free(buf.data); return -1; }
|
||||||
|
|
||||||
|
|||||||
+29
-5
@@ -283,7 +283,14 @@ queue_buffer(struct MHD_Connection *conn, unsigned int status,
|
|||||||
enum MHD_Result ret;
|
enum MHD_Result ret;
|
||||||
|
|
||||||
resp = MHD_create_response_from_buffer(size, (void *)data, mm);
|
resp = MHD_create_response_from_buffer(size, (void *)data, mm);
|
||||||
if (!resp) return MHD_NO;
|
if (!resp) {
|
||||||
|
/* MUST_FREE hands ownership to MHD on success; on failure we still
|
||||||
|
own it and have to free it ourselves or the caller's strdup'd
|
||||||
|
JSON leaks. PERSISTENT/MUST_COPY buffers are caller-owned and
|
||||||
|
we leave them alone. */
|
||||||
|
if (mm == MHD_RESPMEM_MUST_FREE) free((void *)data);
|
||||||
|
return MHD_NO;
|
||||||
|
}
|
||||||
|
|
||||||
MHD_add_response_header(resp, MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, "*");
|
MHD_add_response_header(resp, MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, "*");
|
||||||
MHD_add_response_header(resp, MHD_HTTP_HEADER_CACHE_CONTROL, "no-store");
|
MHD_add_response_header(resp, MHD_HTTP_HEADER_CACHE_CONTROL, "no-store");
|
||||||
@@ -1129,7 +1136,11 @@ write_job_state(const dl_job_t *job) {
|
|||||||
ps[] states and done_bytes. Called with the pool lock held. */
|
ps[] states and done_bytes. Called with the pool lock held. */
|
||||||
static void
|
static void
|
||||||
seed_from_sidecar(dl_job_t *job) {
|
seed_from_sidecar(dl_job_t *job) {
|
||||||
char path[320], buf[16384], murl[768];
|
/* 8 KB sidecar buffer: 4096-piece cap × 2 hex chars / 8 bits = 1024 hex
|
||||||
|
chars for the bitmap, plus the JSON wrapper and the up-to-768-byte
|
||||||
|
manifest_url — fits with room. Halved from 16 KB to lighten the
|
||||||
|
per-admit stack frame on the pool worker. */
|
||||||
|
char path[320], buf[8192], murl[768];
|
||||||
FILE *f;
|
FILE *f;
|
||||||
size_t n;
|
size_t n;
|
||||||
int nbytes = (job->mf.count + 7) / 8;
|
int nbytes = (job->mf.count + 7) / 8;
|
||||||
@@ -2341,11 +2352,16 @@ patchdl_websrv_start(unsigned short port) {
|
|||||||
MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_THREAD_PER_CONNECTION,
|
MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_THREAD_PER_CONNECTION,
|
||||||
port, NULL, NULL, &on_request, NULL,
|
port, NULL, NULL, &on_request, NULL,
|
||||||
MHD_OPTION_NOTIFY_COMPLETED, request_completed, NULL,
|
MHD_OPTION_NOTIFY_COMPLETED, request_completed, NULL,
|
||||||
/* DoS guards: bound concurrent sockets + per-IP to keep a misbehaving
|
/* DoS guards + RAM caps: the PS5 process budget is a few hundred MB
|
||||||
LAN client from exhausting pthreads on the PS5. */
|
and the default pthread stack on this libc is multiple MB. We're
|
||||||
MHD_OPTION_CONNECTION_LIMIT, (unsigned int)64,
|
a single-user UI — 8 concurrent connections is plenty for the
|
||||||
|
SSE poll + a couple of AJAX, and 256 KB per worker is more than
|
||||||
|
enough for our handlers (largest stack use is a 16 KB sidecar
|
||||||
|
buffer in seed_from_sidecar). */
|
||||||
|
MHD_OPTION_CONNECTION_LIMIT, (unsigned int)8,
|
||||||
MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int)8,
|
MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int)8,
|
||||||
MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int)30,
|
MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int)30,
|
||||||
|
MHD_OPTION_THREAD_STACK_SIZE, (size_t)(512 * 1024),
|
||||||
MHD_OPTION_END);
|
MHD_OPTION_END);
|
||||||
|
|
||||||
if (!web_daemon) {
|
if (!web_daemon) {
|
||||||
@@ -2398,11 +2414,19 @@ patchdl_websrv_stop(void) {
|
|||||||
for (int s = 0; s < g_pool.n_workers; s++)
|
for (int s = 0; s < g_pool.n_workers; s++)
|
||||||
pthread_join(g_pool.workers[s], NULL);
|
pthread_join(g_pool.workers[s], NULL);
|
||||||
g_pool.n_workers = 0;
|
g_pool.n_workers = 0;
|
||||||
|
/* Free every job still in the pool list. Workers have joined so nobody
|
||||||
|
else touches the list. free_job_locked unlinks + frees mf.pieces[i].url,
|
||||||
|
mf.pieces, ps, bitmap, and closes the fd. */
|
||||||
|
pthread_mutex_lock(&g_pool.mtx);
|
||||||
|
while (g_pool.jobs) free_job_locked(g_pool.jobs);
|
||||||
|
pthread_mutex_unlock(&g_pool.mtx);
|
||||||
patchdl_net_global_cleanup();
|
patchdl_net_global_cleanup();
|
||||||
|
|
||||||
pthread_mutex_lock(&g_mutex);
|
pthread_mutex_lock(&g_mutex);
|
||||||
patchdl_scan_free(g_titles, g_title_count);
|
patchdl_scan_free(g_titles, g_title_count);
|
||||||
g_titles = NULL;
|
g_titles = NULL;
|
||||||
g_title_count = 0;
|
g_title_count = 0;
|
||||||
|
free(g_debug_json);
|
||||||
|
g_debug_json = NULL;
|
||||||
pthread_mutex_unlock(&g_mutex);
|
pthread_mutex_unlock(&g_mutex);
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user