From 261044b155bf3722770e9dee7fce99e3f81256fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Gro=C3=9Fmann?= Date: Sat, 8 Aug 2026 11:14:34 +0200 Subject: [PATCH 1/2] Decode the whole route block in HTTPDSRV, and call it MOD /.dsrv's HTTPCGI field table stopped at +0010, so the four fields the per-route auth policy added in #98 -- auth, resattr, resclass, resname -- were in the hex dump but never named. The row it did show, login, is the legacy byte, which leaves the table unable to answer the one question it would be opened for: /zosmf/info reads "Login Required 0" and answers 401, and nothing in the table says what decides that. How thoroughly unreadable the block was without labels is visible in the issue itself: the dump there has 01 at +0014, which is HTTP_AUTH_NONE -- that route is public as far as httpd's gate is concerned, and its 401 comes from mvsMF's own auth, not from here. The byte was read as "auth required" because a bare 01 in a hex dump does not say otherwise. All ten fields are named now. auth and resattr are spelled out rather than printed as bare numbers, because neither value means what it looks like: AUTH=DEFAULT is not "no authentication", it means the route carried no AUTH= keyword and inherits the global LOGIN policy, and resattr 0 is not "no access", it is the unset value racf_auth() reads as READ. login keeps its row -- it is still in the block and still in the hex -- but is labelled as the legacy field that no longer decides. The vocabulary follows. The Parmlib keywords have been MOD= and LOC= since the Parmlib configuration went in, so the target is ?target=MOD, the headings read "Route Array" and "Route #n (MOD|LOC)", and the row labels are route->. No CGI alias: the maintainer confirmed there are no consumers of the old spelling. One ordering detail: target matches as a prefix, so the MOD test sits after the MGR test and ?target=M keeps resolving to MGR as it always has. The HTTPD block table had the same drift, so it is audited in the same pass. The struct is 320 bytes and the table stopped at +009C: docroot, codepage, the keep-alive and session-timeout settings, the SMF level and type, the UFS/debug enable flags, the bind retry counts, the client array and the credential handles were all dumped and none was named. The reserved slots left by the 4.0.0 removals are listed too, so the table can be walked against the hex without a gap. credkey is a plain pointer on purpose and not a /.dm link -- that link would be a one-click dump of the blowfish key. Two rows used to disappear when their field was NULL (dbg, socket_thread), which is the same defect in miniature: an offset that vanishes from a table claiming to explain the block. They are always present now, linked only when there is something to link to. Docs: CLAUDE.md's display-module section loses the known-bug paragraph this fixes and gains a note on reading auth vs login, the target table says MOD, and the two struct sizes are corrected -- HTTPD is 320 bytes (0x140), not 288, and HTTPCGI is 32, not 20. Fixes #146 --- CLAUDE.md | 16 +-- src/httpdsrv.c | 356 ++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 303 insertions(+), 69 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 9b1f35c..4a199a7 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -115,13 +115,13 @@ httpd.c (main / initialize) ### Data Structures -**HTTPD (288 bytes, 0x120):** Server-wide singleton. Listener socket, worker pool manager, CGI table, config values, UFS handle, Lua state, FTPD handle, MQTT telemetry handle, stats arrays. (Will shrink as confirmed removals are implemented.) +**HTTPD (320 bytes, 0x140):** Server-wide singleton. Listener socket, worker pool manager, route table, config values, UFS handle, docroot, codepage, keep-alive settings, credential key/array, stats counters. Slots freed by the 4.0.0 removals are kept as `unused_nn` placeholders so no offset moves. **HTTPC (4,096 bytes):** Per-client session. Allocated on accept(), freed on close. Fixed layout with 4,008-byte inline buffer (CBUFSIZE). Contains state machine position, socket, environment variables, file handles, credential. **HTTPX (~270 bytes):** Function vector table. CGI modules call all server functions through this vector — they never link directly to HTTPD code. **Never change existing offsets** — only append new function pointers at the end. -**HTTPCGI (20 bytes):** CGI path-to-program mapping. URL pattern → load module name. +**HTTPCGI (32 bytes):** One route. URL pattern → load module name (`MOD=`) or NULL for a program-less static prefix (`LOC=`), plus the per-route auth policy (`auth`, `resattr`, `resclass`, `resname`). ### Request Processing Pipeline @@ -287,7 +287,7 @@ each one is shown as hex plus a named field table: | `HTTPD` | the server singleton (320 bytes, `0x140`) | no | | `MGR` | `CTHDMGR`, the worker pool manager | no | | `FS` | `UFSSYS` handle (8 bytes with the libufs stub) | no | -| `CGI` | one `HTTPCGI` route, or the route array | **yes** | +| `MOD` | the route array — every `MOD=` / `LOC=` entry | **yes** | | `TASK` | a `CTHDTASK` | **yes** | | `FILE` | a `FILE` handle | **yes** | @@ -316,8 +316,8 @@ Note for anyone reading `jesst.c`: its output goes through `printf`, and in a module `stdout` reaches the HTTP client — so those `printf`s *are* the response body, not log output. -**Known display bug:** the `HTTPCGI` field table in `httpdsrv.c` predates the -per-route auth policy. It shows `login` (legacy) and omits `auth` / `resattr` / -`resclass` / `resname`, so on an authorization question it can say the opposite -of the truth — `/zosmf/info` reports `login 0` while answering 401. Read the hex -at `+14`. Tracked as issue #146. +**Reading the route table:** `?target=MOD` decodes the whole 32-byte `HTTPCGI`, +so `auth` (`+14`) is named and spelled out — that is the field the request is +gated on, not `login` (`+09`), which is the legacy byte and is labelled as such. +`AUTH=DEFAULT` means the route carried no `AUTH=` keyword and falls back to the +global `LOGIN` policy, which is not the same as "no authentication". diff --git a/src/httpdsrv.c b/src/httpdsrv.c index 5fb019c..1016f19 100644 --- a/src/httpdsrv.c +++ b/src/httpdsrv.c @@ -9,7 +9,7 @@ static int getself(char *jobname, char *jobid); #endif static int display_httpd(HTTPD *httpd, HTTPC *httpc); -static int display_cgi(HTTPD *httpd, HTTPC *httpc); +static int display_route(HTTPD *httpd, HTTPC *httpc); static int display_file(HTTPD *httpd, HTTPC *httpc); static int display_fs(HTTPD *httpd, HTTPC *httpc); static int display_mgr(HTTPD *httpd, HTTPC *httpc); @@ -19,7 +19,7 @@ static int display_workers(HTTPD *httpd, HTTPC *httpc); static int display_ufs(HTTPD *httpd, HTTPC *httpc, UFS *ufs); static int display_ufssys(HTTPD *httpd, HTTPC *httpc, UFSSYS *sys); -static int display_cgi_row(HTTPD *httpd, HTTPC *httpc, HTTPCGI *cgi, unsigned n); +static int display_route_row(HTTPD *httpd, HTTPC *httpc, HTTPCGI *route, unsigned n); static int display_worker_row(HTTPD *httpd, HTTPC *httpc, CTHDWORK *worker, unsigned n); static int display_queue_data(HTTPD *httpd, HTTPC *httpc, CTHDQUE *q); #if 0 /* ufs370 internal types -- not available with libufs stub */ @@ -58,11 +58,6 @@ int main(int argc, char **argv) if (!target) target = "HTTPD"; len = strlen(target); - if (http_cmpn(target, "CGI", len)==0) { - display_cgi(httpd, httpc); - goto quit; - } - if (http_cmpn(target, "FILE", len)==0) { display_file(httpd, httpc); goto quit; @@ -78,6 +73,15 @@ int main(int argc, char **argv) goto quit; } + /* The route array (MOD= programs and LOC= static prefixes). Was + "target=CGI" before the Parmlib keywords replaced that vocabulary. + Tested after MGR on purpose: target matches as a prefix, so a bare + "?target=M" has always resolved to MGR and still does. */ + if (http_cmpn(target, "MOD", len)==0) { + display_route(httpd, httpc); + goto quit; + } + if (http_cmpn(target, "HTTPD", len)==0) { display_httpd(httpd, httpc); goto quit; @@ -222,6 +226,13 @@ display_httpd(HTTPD *httpd, HTTPC *httpc) "%p\n", 8, httpx, sizeof(HTTPX), httpx); + http_printf(httpc, + "+%04X" + "httpd->httpc" + "HTTP Client Array (%u)" + "%p\n", + O(httpc), array_count(&httpd->httpc), httpd->httpc); + u = (UCHAR*)&httpd->addr; http_printf(httpc, "+%04X" @@ -245,15 +256,29 @@ display_httpd(HTTPD *httpd, HTTPC *httpc) O(listen), httpd->listen); /* stats file handle removed in 4.0.0 — SMF recording */ + http_printf(httpc, + "+%04X" + "httpd->unused_1c" + "(reserved)" + "%p\n", + O(unused_1c), httpd->unused_1c); if (httpd->dbg) { - http_printf(httpc, + http_printf(httpc, "+%04X" "httpd->dbg" "HTTP Debug File Handle" - "%p\n", + "%p\n", O(dbg), httpd->dbg, httpd->dbg); } + else { + http_printf(httpc, + "+%04X" + "httpd->dbg" + "HTTP Debug File Handle" + "%p\n", + O(dbg), httpd->dbg); + } http_printf(httpc, "+%04X" @@ -327,14 +352,29 @@ display_httpd(HTTPD *httpd, HTTPC *httpc) } http_printf(httpc, "\n"); + http_printf(httpc, + "+%04X" + "httpd->unused" + "(reserved)" + "%02X\n", + O(unused), httpd->unused); + if (httpd->socket_thread) { - http_printf(httpc, + http_printf(httpc, "+%04X" "httpd->socket_thread" "Socket Thread Handle" - "%p\n", + "%p\n", O(socket_thread), httpd->socket_thread, httpd->socket_thread); } + else { + http_printf(httpc, + "+%04X" + "httpd->socket_thread" + "Socket Thread Handle" + "%p\n", + O(socket_thread), httpd->socket_thread); + } http_printf(httpc, "+%04X" @@ -352,8 +392,8 @@ display_httpd(HTTPD *httpd, HTTPC *httpc) http_printf(httpc, "+%04X" - "httpd->httpcgi" - "Common Gateway Interface Array" + "httpd->httpcgi" + "Route Array (MOD= programs, LOC= static prefixes)" "%p\n", O(httpcgi), httpd->httpcgi, httpd->httpcgi); @@ -364,6 +404,13 @@ display_httpd(HTTPD *httpd, HTTPC *httpc) "%-24.24s\n", O(uptime), ctime64(&httpd->uptime)); + http_printf(httpc, + "+%04X" + "httpd->unused_50" + "(reserved)" + "%p\n", + O(unused_50), httpd->unused_50); + http_printf(httpc, "+%04X" "httpd->ufssys" @@ -412,16 +459,69 @@ display_httpd(HTTPD *httpd, HTTPC *httpc) "Config Client Timeout Seconds" "%u\n", O(cfg_client_timeout), httpd->cfg_client_timeout); - + /* cfg_st_*_max removed in 4.0.0 — replaced by SMF + counters */ - - http_printf(httpc, + + http_printf(httpc, + "+%04X" + "httpd->smf_level" + "SMF Recording Level" + "%u %s\n", + O(smf_level), httpd->smf_level, + httpd->smf_level == SMF_LEVEL_NONE ? "NONE" : + httpd->smf_level == SMF_LEVEL_ERROR ? "ERROR" : + httpd->smf_level == SMF_LEVEL_AUTH ? "AUTH" : + httpd->smf_level == SMF_LEVEL_ALL ? "ALL" : "(unknown)"); + + http_printf(httpc, + "+%04X" + "httpd->smf_type" + "SMF Record Type" + "%u\n", + O(smf_type), httpd->smf_type); + + http_printf(httpc, + "+%04X" + "httpd->unused_69" + "(reserved)" + "%02X %02X\n", + O(unused_69), httpd->unused_69[0], httpd->unused_69[1]); + + http_printf(httpc, "+%04X" "httpd->cfg_cgictx" "Config CGI Context Pointers" - "%u\n", + "%u\n", O(cfg_cgictx), httpd->cfg_cgictx); - + + http_printf(httpc, + "+%04X" + "httpd->ufs_enabled" + "UFS Filesystem Enabled" + "%u\n", + O(ufs_enabled), httpd->ufs_enabled); + + http_printf(httpc, + "+%04X" + "httpd->dbg_enabled" + "Debug Output Enabled" + "%u\n", + O(dbg_enabled), httpd->dbg_enabled); + + http_printf(httpc, + "+%04X" + "httpd->bind_tries" + "Socket Bind Retry Count" + "%u\n", + O(bind_tries), httpd->bind_tries); + + http_printf(httpc, + "+%04X" + "httpd->bind_sleep" + "Socket Bind Retry Delay (seconds)" + "%u\n", + O(bind_sleep), httpd->bind_sleep); + http_printf(httpc, "+%04X" "httpd->total_requests" @@ -449,7 +549,14 @@ display_httpd(HTTPD *httpd, HTTPC *httpc) "Active Connections" "%u\n", O(active_connections), httpd->active_connections); - + + http_printf(httpc, + "+%04X" + "httpd->unused_80" + "(reserved)" + "%p\n", + O(unused_80), httpd->unused_80); + http_printf(httpc, "+%04X" "httpd->unused_84" @@ -477,7 +584,14 @@ display_httpd(HTTPD *httpd, HTTPC *httpc) "Unix \"like\" File System Handle" "%p\n", O(ufs), httpd->ufs); - + + http_printf(httpc, + "+%04X" + "httpd->unused_94" + "(reserved)" + "%p\n", + O(unused_94), httpd->unused_94); + http_printf(httpc, "+%04X" "httpd->self" @@ -492,6 +606,71 @@ display_httpd(HTTPD *httpd, HTTPC *httpc) "%p\n", O(cgictx), httpd->cgictx, (HTTPD_CGICTX_MAX+1)*4, httpd->cgictx); + http_printf(httpc, + "+%04X" + "httpd->docroot" + "UFS Document Root Prefix" + "\"%.128s\"\n", + O(docroot), httpd->docroot); + + http_printf(httpc, + "+%04X" + "httpd->listen_queue" + "Listen Backlog" + "%u\n", + O(listen_queue), httpd->listen_queue); + + http_printf(httpc, + "+%04X" + "httpd->unused_121" + "(reserved, alignment padding)" + "%02X %02X %02X\n", + O(unused_121), httpd->unused_121[0], httpd->unused_121[1], + httpd->unused_121[2]); + + http_printf(httpc, + "+%04X" + "httpd->codepage" + "Codepage Name" + "\"%.16s\"\n", + O(codepage), httpd->codepage); + + http_printf(httpc, + "+%04X" + "httpd->cfg_keepalive_timeout" + "Keep-Alive Idle Timeout (seconds)" + "%u\n", + O(cfg_keepalive_timeout), httpd->cfg_keepalive_timeout); + + http_printf(httpc, + "+%04X" + "httpd->cfg_keepalive_max" + "Max Requests per Connection" + "%u\n", + O(cfg_keepalive_max), httpd->cfg_keepalive_max); + + http_printf(httpc, + "+%04X" + "httpd->cfg_session_timeout" + "Credential Idle TTL (minutes, 0 = off)" + "%u\n", + O(cfg_session_timeout), httpd->cfg_session_timeout); + + /* credkey is the blowfish key: report the pointer, never a storage link */ + http_printf(httpc, + "+%04X" + "httpd->credkey" + "Credential Key Handle" + "%p\n", + O(credkey), httpd->credkey); + + http_printf(httpc, + "+%04X" + "httpd->credarr" + "Credential Array Handle" + "%p\n", + O(credarr), httpd->credarr); + http_printf(httpc, "\n"); send_last(httpd, httpc); @@ -1403,13 +1582,13 @@ struct ufs { #ifdef O #undef O #endif -#define O(a) ((unsigned)&(cgi->a) - (unsigned)cgi) +#define O(a) ((unsigned)&(route->a) - (unsigned)route) static int -display_cgi(HTTPD *httpd, HTTPC *httpc) +display_route(HTTPD *httpd, HTTPC *httpc) { int rc = 0; - HTTPCGI *cgi = NULL; + HTTPCGI *route = NULL; HTTPCGI **array = NULL; char *memory = NULL; unsigned n, count; @@ -1428,20 +1607,20 @@ display_cgi(HTTPD *httpd, HTTPC *httpc) array = (HTTPCGI**) strtoul(memory, NULL, 16); count = array_count(&array); - http_printf(httpc, "

CGI Array %p

", array); + http_printf(httpc, "

Route Array %p

", array); #if 0 - http_printf(httpc, + http_printf(httpc, "\n", - "CGI%20Array", array, count*sizeof(HTTPCGI*)); + "Route%20Array", array, count*sizeof(HTTPCGI*)); #endif - display_memory(httpd, httpc, "CGI Array", array, count*sizeof(HTTPCGI*), 16); - + display_memory(httpd, httpc, "Route Array", array, count*sizeof(HTTPCGI*), 16); + for(n=0; n < count; n++) { - cgi = array[n]; - - if (!cgi) continue; - display_cgi_row(httpd, httpc, cgi, n); + route = array[n]; + + if (!route) continue; + display_route_row(httpd, httpc, route, n); } done: @@ -1451,16 +1630,46 @@ display_cgi(HTTPD *httpd, HTTPC *httpc) return 0; } -static int -display_cgi_row(HTTPD *httpd, HTTPC *httpc, HTTPCGI *cgi, unsigned n) +/* auth_mode_text() - decode HTTPCGI.auth (HTTP_AUTH_*). DEFAULT is the value +** a route carries when it had no AUTH= keyword, so it is not "no auth" -- the +** request still runs through the legacy global LOGIN policy. */ +static const char * +auth_mode_text(UCHAR auth) +{ + switch (auth) { + case HTTP_AUTH_DEFAULT: return "DEFAULT (inherits the global LOGIN policy)"; + case HTTP_AUTH_NONE: return "NONE (public, never challenged)"; + case HTTP_AUTH_FORM: return "FORM (HTML login form)"; + case HTTP_AUTH_BASIC: return "BASIC (401 WWW-Authenticate)"; + default: return "(unknown)"; + } +} + +/* racf_attr_text() - decode HTTPCGI.resattr. 0 is the common value: httpprm +** only sets resattr when RES= is present, and racf_auth() assumes READ for 0. */ +static const char * +racf_attr_text(UCHAR attr) +{ + switch (attr) { + case 0: return "READ (assumed, RESATTR not set)"; + case RACF_ATTR_READ: return "READ"; + case RACF_ATTR_UPDATE: return "UPDATE"; + case RACF_ATTR_CONTROL: return "CONTROL"; + case RACF_ATTR_ALTER: return "ALTER"; + default: return "(unknown)"; + } +} + +static int +display_route_row(HTTPD *httpd, HTTPC *httpc, HTTPCGI *route, unsigned n) { char title[40]; - - sprintf(title, "CGI #%u", n); + + sprintf(title, "Route #%u (%s)", n, route->pgm ? "MOD" : "LOC"); http_printf(httpc, "

%s

\n", title); - display_memory(httpd, httpc, title, cgi, sizeof(HTTPCGI), 16); + display_memory(httpd, httpc, title, route, sizeof(HTTPCGI), 16); http_printf(httpc, "\n"); @@ -1468,42 +1677,66 @@ display_cgi_row(HTTPD *httpd, HTTPC *httpc, HTTPCGI *cgi, unsigned n) "" "" "\n"); - + http_printf(httpc, "" - "" + "" "" - "\n", - O(eye), cgi->eye); + "\n", + O(eye), route->eye); http_printf(httpc, "" - "" + "" "" - "\n", - O(wild), cgi->wild); + "\n", + O(wild), route->wild); http_printf(httpc, "" - "" - "" - "\n", - O(login), cgi->login); + "" + "" + "\n", + O(login), route->login); http_printf(httpc, "" - "" + "" "" - "\n", - O(len), cgi->len); + "\n", + O(len), route->len); http_printf(httpc, "" - "" + "" "" - "\n", - O(path), cgi->path); + "\n", + O(path), route->path ? route->path : "(none)"); + + http_printf(httpc, "" + "" + "" + "\n", + O(pgm), route->pgm ? route->pgm : "(none)"); + + http_printf(httpc, "" + "" + "" + "\n", + O(auth), route->auth, auth_mode_text(route->auth)); + + http_printf(httpc, "" + "" + "" + "\n", + O(resattr), route->resattr, racf_attr_text(route->resattr)); http_printf(httpc, "" - "" - "" + "" + "" "\n", - O(pgm), cgi->pgm ? cgi->pgm : "(none)"); + O(resclass), route->resclass ? route->resclass : "(none)"); + + http_printf(httpc, "" + "" + "" + "\n", + O(resname), route->resname ? route->resname : "(none)"); #if 0 http_printf(httpc, "" @@ -2355,7 +2588,7 @@ display_help(HTTPD *httpd, HTTPC *httpc) http_printf(httpc, "

HTTPDSRV Help

\n"); http_printf(httpc, "

Usage: http:/%s%s?target=name[&m=nnnnnnnn]

\n", host, path); - http_printf(httpc, "

This CGI program uses the QUERY variable " + http_printf(httpc, "

This module uses the QUERY variable " "\"target\" value to control which HTTPD storage area to display.

\n"); http_printf(httpc, "

\n"); @@ -2364,9 +2597,6 @@ display_help(HTTPD *httpd, HTTPC *httpc) http_printf(httpc, "
Display the HTTPD storage areas. " "This is the default if ?target is omitted.

\n"); - http_printf(httpc, "
?target=CGI&m=nnnnnnnn
\n"); - http_printf(httpc, "
Display the Common Gateway Interface array at the memory address.

\n"); - http_printf(httpc, "
?target=FILE&m=nnnnnnnn
\n"); http_printf(httpc, "
Display the File Handle at the memory address.

\n"); @@ -2379,6 +2609,10 @@ display_help(HTTPD *httpd, HTTPC *httpc) http_printf(httpc, "
?target=MGR&m=nnnnnnnn
\n"); http_printf(httpc, "
Display the Thread Manager Handle at the memory address.

\n"); + http_printf(httpc, "
?target=MOD&m=nnnnnnnn
\n"); + http_printf(httpc, "
Display the route array (MOD= programs and LOC= static " + "prefixes) at the memory address. Was ?target=CGI.

\n"); + http_printf(httpc, "
?target=TASK&m=nnnnnnnn
\n"); http_printf(httpc, "
Display the Thread Task Handle at the memory address.

\n"); From 27f4fe6bedfbd3f1ef22e6f0684a4861d0f394d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Gro=C3=9Fmann?= Date: Sat, 8 Aug 2026 18:00:42 +0200 Subject: [PATCH 2/2] Name the codepage actually in effect, not the empty string The row added a commit ago printed httpd->codepage verbatim, and on a system with no CODEPAGE keyword that is "" -- which reads as "none set" when it means CP037. set_defaults() leaves the field empty (httpprm.c:137), http_prm passes that through as NULL (httpprm.c:62), and http_xlate_init() maps NULL to the CP037 tables (httpxlat.c:395). That is the same shape as the login row this branch is fixing: a raw value that does not say what is in force. An empty codepage now renders as CP037 with the reason, the same way resattr 0 renders as READ. Relates to #146 --- src/httpdsrv.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/httpdsrv.c b/src/httpdsrv.c index 1016f19..a93af7a 100644 --- a/src/httpdsrv.c +++ b/src/httpdsrv.c @@ -628,12 +628,25 @@ display_httpd(HTTPD *httpd, HTTPC *httpc) O(unused_121), httpd->unused_121[0], httpd->unused_121[1], httpd->unused_121[2]); - http_printf(httpc, - "
" - "" - "" - "\n", - O(codepage), httpd->codepage); + /* An empty codepage is the default, not "none": set_defaults() leaves it + ** empty and http_xlate_init() reads that as CP037. Say so -- a bare "" + ** in the table would not tell a reader which tables are actually loaded. */ + if (httpd->codepage[0]) { + http_printf(httpc, + "" + "" + "" + "\n", + O(codepage), httpd->codepage); + } + else { + http_printf(httpc, + "" + "" + "" + "\n", + O(codepage)); + } http_printf(httpc, ""
Data NameDescriptionContents
+%04Xcgi->eyeroute->eyeEye Catcher\"%s\"
\"%s\"
+%04Xcgi->wildroute->wildIs Wildcard%u
%u
+%04Xcgi->loginLogin Required%u
route->loginLogin Required (legacy -- route->auth decides)%u
+%04Xcgi->lenroute->lenPath Length%u
%u
+%04Xcgi->pathroute->pathPath Name\"%s\"
\"%s\"
+%04Xroute->pgmProgram Name (NULL = LOC static route)\"%s\"
+%04Xroute->authAuth Mode (AUTH=)%u %s
+%04Xroute->resattrRACF Access Attribute%02X %s
+%04Xcgi->pgmProgram Nameroute->resclassRACF Class (RES=, NULL = no resource gate)\"%s\"
+%04Xroute->resnameRACF Resource Name (RES=)\"%s\"
----------
+%04Xhttpd->codepageCodepage Name\"%.16s\"
+%04Xhttpd->codepageCodepage Name (CODEPAGE=)\"%.16s\"
+%04Xhttpd->codepageCodepage Name (CODEPAGE=)\"\" CP037 (default, no CODEPAGE keyword)
+%04X