diff --git a/Makefile b/Makefile index a25ecfe..a2b152b 100644 --- a/Makefile +++ b/Makefile @@ -1,2 +1,85 @@ MBT_ROOT := mbt include $(MBT_ROOT)/mk/mbt.mk + +# -- Webroot disk (issue #252) -------------------------------------- +# static/ ships as a formatted UFS370 image, because a UFS disk cannot +# travel any of the ways the rest of the package does: SMP has no element +# type for a DSORG=PS/RECFM=U image and must never touch site content, and +# TSO RECEIVE allocates its own target and refuses to merge into an +# existing dataset -- a first-install-only transport for something that +# gets updated. The operator uploads the image with IND$FILE instead; +# docs/installation.md carries the procedure. +# +# There is deliberately no codepage handling here. http_send_file() +# translates UFS files with the hard-coded IBM-1047 table, independent of +# the CODEPAGE= setting (src/httpfile.c), and IBM-1047 is what +# `ufsd-utils cp` writes -- measured byte for byte over the whole file, +# UTF-8 sequences included. Only 0x85 and 0xF7 fail to round-trip, so a +# text file must avoid characters whose UTF-8 encoding contains them. +# +# ufsd-utils is pinned rather than taken from PATH: `create` stamps an +# owner and a timestamp into the image, so the version that builds a +# release artifact is part of what is shipped. Release CI runs the shared +# mbt workflow and cannot install anything of its own, hence the fetch into +# .mbt/tools/. Pass UFSD_UTILS= to build against a different one. +# 1M is 256 blocks of 4096, which the inode list caps at 62 files -- the same +# 62 any disk up to 512 blocks gets, so a larger image buys space, not files. +# Owner and group are metadata: UFSD enforces the MOUNT's OWNER(), not the +# inode owner. They are named anyway, so a release artifact does not carry +# whatever userid happened to build it. +# +# It is built under $(BUILDDIR) and NOT under $(DISTDIR), which is not a +# detail: the release workflow publishes `dist/*` as GitHub Release assets, so +# an image sitting there would appear beside the versioned artifacts under a +# name carrying no version at all -- and as a second copy of what the archive +# already holds. The archive is where it belongs, next to the README and the +# jobs an operator needs with it. +WEBROOT_IMG := $(BUILDDIR)/webroot/httpd-webroot.img +WEBROOT_SRC := static +WEBROOT_SIZE := 1M +WEBROOT_OWNER := IBMUSER +WEBROOT_GROUP := SYSPROG + +UFSD_UTILS_VER := 1.0.1 +UFSD_UTILS_BIN := .mbt/tools/ufsd-utils-$(UFSD_UTILS_VER) +UFSD_UTILS ?= $(UFSD_UTILS_BIN) + +# Only the pinned binary is a prerequisite -- an overridden UFSD_UTILS names +# a program on PATH, which make would try (and fail) to build as a file. +WEBROOT_TOOL_DEP := $(if $(filter $(UFSD_UTILS_BIN),$(UFSD_UTILS)),$(UFSD_UTILS_BIN)) + +$(UFSD_UTILS_BIN): + $(E) "[webroot] fetching ufsd-utils $(UFSD_UTILS_VER)" + @mkdir -p $(dir $@) + @os=`uname -s | tr '[:upper:]' '[:lower:]'`; \ + arch=`uname -m`; \ + case "$$arch" in \ + x86_64|amd64) arch=amd64 ;; \ + arm64|aarch64) arch=arm64 ;; \ + *) echo "[webroot] no ufsd-utils release for $$arch" >&2; exit 1 ;; \ + esac; \ + name=ufsd-utils-$$os-$$arch; \ + curl -sSfL "https://github.com/mvslovers/ufsd-utils/releases/download/v$(UFSD_UTILS_VER)/$$name.tar.gz" \ + | tar xzOf - $$name > $@ || { rm -f $@; exit 1; } + @chmod +x $@ + +webroot: $(WEBROOT_IMG) + +# The directories are prerequisites next to the files: a *new* file is picked +# up either way, because make expands the wildcard when it parses this and the +# file is then newer than the image -- but a *deleted* one leaves an image that +# is newer than everything still there, and it would go on shipping the file. +# A directory's mtime moves when an entry is added or removed, which covers it. +$(WEBROOT_IMG): $(shell find $(WEBROOT_SRC) -type f -o -type d) $(WEBROOT_TOOL_DEP) + $(E) "[webroot] $(notdir $@) ($(WEBROOT_SIZE), from $(WEBROOT_SRC)/)" + @mkdir -p $(dir $@) + @rm -f $@ + $(Q)$(UFSD_UTILS) create $@ --size $(WEBROOT_SIZE) --blksize 4096 \ + --owner $(WEBROOT_OWNER) --group $(WEBROOT_GROUP) > /dev/null + $(Q)$(UFSD_UTILS) cp -r $(WEBROOT_SRC)/ $@:/ + +# Both read it: 'package' ships it in the archive ([distribution] extra), +# and 'dist' re-renders that archive on its own. +package dist: webroot + +.PHONY: webroot diff --git a/TODO.md b/TODO.md index de659e6..fbcad28 100644 --- a/TODO.md +++ b/TODO.md @@ -11,9 +11,9 @@ stops. `CLAUDE.md` forbids a task list in itself because a copy of a tracker is wrong the first time someone closes something, and the only defence that works is to hold nothing worth going stale. -*Last reconciled against the tracker: 2026-08-23, three issues open (#250 filed -that day; #237 closed by PR #249, and before it #245 by PR #248, #233 by -PR #244, #242 by PR #246 and #243 by PR #247).* +*Last reconciled against the tracker: 2026-08-24, three issues open (#252 filed +and closed the same day by PR #253; before it #250, #237 by PR #249, #245 by +PR #248, #233 by PR #244, #242 by PR #246 and #243 by PR #247).* --- @@ -39,6 +39,12 @@ exit that could end a refused start `CC 0000`; nothing in that thread is open, and `docs/messages.md` §*A refused start* is where the resulting contract is written down. +**What is left before the 4.0.0 tag is not in the tracker at all** — it is the +install package, and [`smp-todo.md`](smp-todo.md) holds it: the `THTP400` check +against CDS and ACDS on both stands (O1), and the dry run under the throwaway +FMID (O2). O3 closed with #252, which put the document root in the archive as a +UFS image; what remains of it is a checksum for the release assets. + --- ### 1 · #250 — does a LINKed module survive in the Job Pack Area? diff --git a/docs/installation.md b/docs/installation.md index 3ce6864..0aa971a 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -6,7 +6,7 @@ Hercules build) from the release distribution archive. The installation is managed by **SMP Release 4** — the SMP that ships with MVS 3.8j, not SMP/E. That means the system keeps a record of what was installed, and there is a supported way back out (see -[Removing HTTPD](#11-removing-httpd)). +[Removing HTTPD](#12-removing-httpd)). ## Getting help @@ -15,7 +15,7 @@ installed, and there is a supported way back out (see That is the only place bug reports are tracked. Please include the two build stamps HTTPD writes at startup (`HTTPD000I` and `HTTPD005I`, see -[step 9](#9-start-and-verify)) — they identify the exact build — plus the +[step 10](#10-start-and-verify)) — they identify the exact build — plus the console messages and, for an install problem, the job output. **Questions and general support** are on Discord: @@ -44,6 +44,7 @@ recognise which file is which. | `httpd--samplib.xmit` | the sample library: STC procedure and configuration pattern | | `httpd--alloc.jcl` | allocates the datasets SMP installs into — **run once** | | `httpd--inst.jcl` | receives everything and installs it — repeatable | +| `httpd-webroot.img` | the document root as a UFS370 disk image — outside SMP, see [step 9](#9-the-webroot-disk) | The load library holds five modules: @@ -73,8 +74,9 @@ See [step 7](#7-install-the-procedure-and-the-configuration). > and removing the other four changes no behaviour of a server that never > routed to them. -The sample library holds two members, `HTTPD` (the started task procedure) and -`HTTPPRM0` (the configuration pattern). +The sample library holds three members: `HTTPD` (the started task procedure), +`HTTPPRM0` (the configuration pattern) and `HTTPWEBR` (a job that allocates the +dataset the webroot image is uploaded to). Where everything ends up: @@ -82,8 +84,12 @@ Where everything ends up: HTTPD..LINKLIB the load modules -- point STEPLIB here HTTPD..SAMPLIB the patterns you copy from in step 7 HTTPD..AHTTPLOD SMP's distribution library, the base a RESTORE returns to +HTTPD..WEBROOT.UFS the document root, if you upload the shipped one (step 9) ``` +The last one is not SMP's: it is a filesystem image, and it holds site content +that an `APPLY` must never touch. Step 9 is where it comes from. + --- ## 2. Prerequisites @@ -94,7 +100,8 @@ HTTPD..AHTTPLOD SMP's distribution library, the base a RESTORE returns t - **RAKF** — see [Authorisation](#authorisation) below. HTTPD does not start without either RAKF or an APF list entry, and no configuration option turns that off. -- **UFSD**, only if you want to serve static files — see below. +- **UFSD**, only if you want to serve static files — see below. The archive + ships a document root for it as a disk image (step 9). - **mvsMF**, only if you want the REST API the shipped configuration routes to — see below. - A userid authorised to submit jobs, to update a PROCLIB in the started-task @@ -129,6 +136,10 @@ the start. On a deployment that will never have UFSD, `UFS=0` in the configuration member skips the initialisation altogether and the warning with it. +With UFSD there, the archive has a document root ready for it — +`httpd-webroot.img`, a formatted filesystem holding the welcome page, which +[step 9](#9-the-webroot-disk) uploads and mounts. + Install HTTPD now and add UFSD later if you want it: , with its own guide at [ufsd/docs/installation.md](https://github.com/mvslovers/ufsd/blob/main/docs/installation.md). @@ -246,6 +257,11 @@ primary for the load XMIT (roughly 650 KB), 5 for the sample library, secondary about 10% of each. Method b) below shows this with zowe; TSO or ISPF 3.2 does the same job. +The third file that has to reach the host, `httpd-webroot.img`, is **not** one of +these: it is a filesystem image with its own record format, it is not received +by anything, and it is only needed once the server runs. It has its own step — +[step 9](#9-the-webroot-disk) — and nothing here applies to it. + Pick **one** method: ### a) FTP — mvslovers/ftpd (no pre-allocation) @@ -303,6 +319,8 @@ them unnecessary. No pre-allocation needed. Use your emulator's file transfer in **binary** mode (no ASCII/CRLF translation) with `RECFM=FB LRECL=80 BLKSIZE=3120`. The exact option syntax is client-dependent — consult its file-transfer documentation. +[Step 9](#9-the-webroot-disk) walks an IND$FILE upload through in full, for the +webroot image; the same emulator handling applies here. --- @@ -323,7 +341,7 @@ Expect `COND CODE 0000`. > install, `HTTPD..AHTTPLOD` holds SMP's accepted copy of the modules; a > re-run that scratched it would leave the SMP inventory reporting an install > that is no longer on the system, and nothing would say so. To start over, -> reject the SYSMOD first — see [Removing HTTPD](#11-removing-httpd). +> reject the SYSMOD first — see [Removing HTTPD](#12-removing-httpd). --- @@ -332,7 +350,7 @@ Expect `COND CODE 0000`. Only relevant when you are upgrading. The APPLY writes into `HTTPD..LINKLIB`, and each release has its own — so a running *older* HTTPD does not block the install. It does, however, keep running the old modules until -you restart it (step 9) against the procedure you copy in step 7. +you restart it (step 10) against the procedure you copy in step 7. ``` /P HTTPD @@ -419,6 +437,9 @@ Copy from `HTTPD..SAMPLIB`: `SYS2.PROCLIB` is the usual home for the procedure. +The third member, `HTTPWEBR`, is not copied anywhere: it is a job you submit +straight from the sample library when you get to [step 9](#9-the-webroot-disk). + ### Which PARMLIB The shipped procedure defaults to `D='SYS2.PARMLIB'`. **On TK5 that dataset @@ -576,7 +597,170 @@ client can log in. --- -## 9. Start and verify +## 9. The webroot disk + +`DOCROOT` names a path in the UFSD filesystem, so the pages HTTPD serves live on +a UFS disk and not in a library. The archive carries one ready to use: +`httpd-webroot.img`, a 1 MB UFS370 filesystem holding the welcome page. Three +steps put it on the system — allocate, upload, mount — and none of them belongs +to SMP. + +**Why it travels on its own.** SMP has no element type for a `DSORG=PS`, +`RECFM=U` image, and site content is precisely what an `APPLY` must never +touch. TSO RECEIVE is no better: it allocates its own target and refuses to +merge into an existing dataset, which makes it a first-install-only transport +for something you will replace. So the disk ships as a plain file and goes up +over **IND$FILE**, which needs nothing on the target beyond a 3270 session. + +This step needs **UFSD** (step 2). Without it there is nothing to mount and +nothing static to serve; every `MOD=` route works regardless. + +**Already have a webroot?** Then read this for the mechanics and keep your own +disk. Nothing here can overwrite it — the dataset is named for this release. + +### a) Allocate the dataset + +`HTTPD..SAMPLIB(HTTPWEBR)` does exactly this and nothing else: + +```jcl +//ALLOC EXEC PGM=IEFBR14 +//WEBROOT DD DSN=HTTPD..WEBROOT.UFS,DISP=(NEW,CATLG), +// UNIT=SYSDA,SPACE=(4096,256), +// DCB=(DSORG=PS,RECFM=U,BLKSIZE=4096) +``` + +**Run it before the upload, not after.** IND$FILE allocates an *existing* +dataset `DISP=SHR` and takes the DCB from its label; only when the dataset is +missing does it invent one of its own. Allocating it here is what pins +`BLKSIZE=4096` — the block size the image was formatted with, and the one thing +about the transfer that has to be right. + +`SPACE` is counted in blocks of 4096, so 256 blocks is the megabyte the image +occupies. **Primary extent only**: the filesystem is addressed by block number +within the primary extent, so anything in a secondary would never be reached. + +### b) Upload it with IND$FILE + +The image must arrive byte for byte: **binary**, no ASCII translation, no CRLF +conversion. With the dataset allocated, the command carries no options at all — +which also makes it independent of which IND$FILE build your system has: + +``` +IND$FILE PUT HTTPD.V4R0M0.WEBROOT.UFS +``` + +Binary is IND$FILE's default; `ASCII` and `CRLF` are what you would have to add +to break it. + +Most emulators issue that command for you. x3270, c3270 and wc3270 take it as +one action, and their file-transfer dialog asks for the same fields: + +``` +Transfer(Direction=send, + LocalFile=httpd-webroot.img, + HostFile=HTTPD.V4R0M0.WEBROOT.UFS, + Host=tso, Mode=binary, Exist=replace) +``` + +Leave `Recfm`, `Lrecl`, `Blksize` and the space fields **unset**: the dataset +exists, so its label decides, and an emulator's allocation defaults are not +worth auditing. + +Either way the session has to sit on an input field that can accept the command +before the transfer starts — a cleared TSO **READY** screen, or ISPF option 6. +The emulator types `IND$FILE` into whatever field the cursor is on; it does not +find one for you. + +If you would rather let IND$FILE create the dataset, it needs to be told +everything the allocation job otherwise says: + +``` +IND$FILE PUT HTTPD.V4R0M0.WEBROOT.UFS (RECFM(U) BLKSIZE(4096) TRACKS SPACE(70) +``` + +70 tracks holds a megabyte even on a 3350, where a 4096-byte block packs four to +a track; a 3380 or 3390 needs half that. There is no secondary quantity on +purpose (see above). + +A megabyte over a 3270 session takes minutes rather than seconds. That is the +transfer, not a hang. + +### c) Mount it + +The mount belongs to **UFSD**, not to HTTPD. In its Parmlib member: + +``` +MOUNT DSN(HTTPD.V4R0M0.WEBROOT.UFS) PATH(/www) MODE(RO) +``` + +or, without a restart — note that the operator command spells its operands with +`=` and commas where the Parmlib statement uses parentheses: + +``` +/F UFSD,MOUNT DSN=HTTPD.V4R0M0.WEBROOT.UFS,PATH=/www,MODE=RO +``` + +`/www` is HTTPD's default `DOCROOT`; if you changed it in `HTTPPRM0`, mount it +there instead. `MODE(RO)` is the default and the right setting here — the +shipped disk is product content and nothing in the server writes to it. + +**UFSD first, then HTTPD.** The filesystem has to be up and mounted before the +server that serves from it — a runtime dependency SMP cannot express, which is +why the SYSMOD declares no prerequisite on UFSD at all. + +### d) Verify + +``` +/F UFSD,MOUNT LIST +curl -v http://your-mvs-host:8080/ +``` + +`/` serves `/www/index.html`: HTTPD appends `index.html` to a request for a +directory. + +| What you see | What it means | +|---|---| +| `UFSD062E SUPERBLOCK VALIDATION FAILED FOR …` | the dataset's `BLKSIZE` is not the image's 4096 — or the transfer was not binary | +| `HTTPD044W UNABLE TO INITIALIZE FILE SYSTEM` | UFSD is not running (step 2) | +| `404` on `/` | mounted somewhere other than `DOCROOT` | +| a page of mojibake | the transfer translated the bytes; upload again in binary | + +### Replacing the content later + +**Unmount before you re-upload.** IND$FILE allocates `DISP=SHR`, so it will +happily rewrite a dataset UFSD has open — under the server's own buffers: + +``` +/F UFSD,UNMOUNT PATH=/www + ... upload ... +/F UFSD,MOUNT DSN=HTTPD.V4R0M0.WEBROOT.UFS,PATH=/www,MODE=RO +``` + +For pages of your own, build the image on your workstation with +[ufsd-utils](https://github.com/mvslovers/ufsd-utils) and upload it the same way: + +``` +ufsd-utils create mysite.img --size 1M --blksize 4096 --owner IBMUSER +ufsd-utils cp -r ./mysite/ mysite.img:/ +``` + +Text files are stored in **IBM-1047**, which is what `ufsd-utils` writes by +default and what HTTPD's static file path translates back with — a UFS file is +converted with that table regardless of the `CODEPAGE=` setting, which governs +MVS datasets and the server's own output. So there is nothing to configure here, +and nothing to match up. + +With mvsMF installed there is a second route for single files — +`PUT /zosmf/restfiles/fs/www/index.html` — but only into a filesystem mounted +`MODE(RW)`, which the shipped disk is not. + +One thing the image is not: reproducible. It carries its own creation +timestamp, so two builds of the same commit differ byte for byte. Compare it +against the archive you downloaded, never against one you rebuilt. + +--- + +## 10. Start and verify ``` /S HTTPD default member (HTTPPRM0) @@ -653,7 +837,7 @@ and `DISPLAY TIme` need one and two letters respectively. --- -## 10. What HTTPD serves +## 11. What HTTPD serves Two kinds of route, both declared in the configuration member, both carrying the same per-route auth policy: @@ -679,7 +863,7 @@ writing your own module is --- -## 11. Removing HTTPD +## 12. Removing HTTPD Because the installation is SMP-managed, there is a defined way back — but it is **not** the `RESTORE` followed by `REJECT` that SMP documentation leads you to @@ -779,8 +963,9 @@ Leave `HTTPD..SAMPLIB` alone if you like — the install job's `DELOLD` ste scratches it on its own. **5. What is not removed, because SMP never owned it:** the procedure and the -configuration member you copied in step 7, and your RAKF definitions. Those are -yours to delete. +configuration member you copied in step 7, your RAKF definitions, and the +webroot disk `HTTPD..WEBROOT.UFS` with the `MOUNT` statement that names it. +Those are yours to delete — unmount the disk before you scratch it. > The `RESTORE`/`REJECT` behaviour above was measured on 2026-08-14 against an > accepted FMID from a package built by this same generator, on an MVS/CE system @@ -813,6 +998,8 @@ yours to delete. | A route answers `200` to anyone | It carries no `AUTH=`, or no route claims the path at all. Both are public by design — step 7. `/.dsrv?target=MOD` prints the policy each route actually got | | `401` on a route showing `AUTH=NONE` | Not HTTPD's gate. mvsMF runs its own auth track on `/zosmf/*` — establish which layer answered before debugging HTTPD's | | `S106` at start on a freshly installed library | The XMIT was uploaded in text mode. Re-upload in **binary** and re-run the install job | +| `UFSD062E SUPERBLOCK VALIDATION FAILED` at `MOUNT` | The webroot dataset was not allocated `RECFM=U BLKSIZE=4096`, or the image was uploaded in text mode — step 9 | +| `404` on `/` with UFSD running | The webroot is mounted somewhere other than `DOCROOT`, or nothing is mounted there at all — step 9 | | The step ends `CC 0000` but nothing was ever served | A clean `/P HTTPD` and a refused start are `CC 0000` and `CC 0008`. If it is 0000, the server ran — look for what stopped it | For the complete message reference, see diff --git a/project.toml b/project.toml index ad75ee0..f86692b 100644 --- a/project.toml +++ b/project.toml @@ -283,11 +283,21 @@ headers = [ ] # -- Distribution (SMP4 install package) -------------------------- -# Turns `make package` into an installable product, the same two transports -# ufsd and ftpd ship with: +# Turns `make package` into an installable product, on the two transports ufsd +# and ftpd ship with, plus a third one they have no need for: # # load modules -> XMIT -> staging HTTPLOAD -> SMP ++MOD -> HTTPD..LINKLIB # samplib/ -> XMIT -> TSO RECEIVE -> HTTPD..SAMPLIB +# static/ -> UFS370 image -> IND$FILE -> HTTPD..WEBROOT.UFS +# +# The webroot disk (issue #252) rides neither of the first two, and not because +# of a gap in the tooling: SMP has no element type for a DSORG=PS/RECFM=U image +# and must never touch site content, and TSO RECEIVE allocates its own target +# and refuses to merge into an existing one -- a first-install-only transport +# for something an operator updates. So it travels as a plain file in the +# archive (`extra` below) and goes up over IND$FILE, which needs nothing on the +# target beyond a 3270 session. `make webroot` builds it; the Makefile carries +# the why, docs/installation.md the operator's side. # # SMP *copies* the host-bound modules rather than re-binding them (a COPY-style # ++JCLIN is what says so), which is what keeps HTTPD's ac = 1 and every custom @@ -308,7 +318,11 @@ headers = [ # # Every value is a dataset name; the ddname SMP addresses it by is always the # last qualifier, so LINKLIB, SAMPLIB, HTTPLOAD and AHTTPLOD fall out of the -# names. @VRM@ becomes V4R0M0 at package time (V4R0M0D while the version still +# names. The webroot is the one name with a fourth qualifier +# (HTTPD.@VRM@.WEBROOT.UFS) and it is deliberate: no ddname is derived from it +# because nothing here allocates or receives it, and .UFS says at a glance that +# the dataset is a filesystem image rather than a library. Do not normalise it +# into the three-qualifier pattern -- it is not part of that scheme. @VRM@ becomes V4R0M0 at package time (V4R0M0D while the version still # carries -dev). ufsd/ftpd spell these LOAD / ALOD, which for # a five-letter product would be nine characters; HTTPLOAD/AHTTPLOD keeps the # suffix and drops the letter, rather than keeping HTTPD and truncating the @@ -327,6 +341,21 @@ headers = [ # package tells the operator to read a file it does not contain. readme = "docs/installation.md" +# The webroot disk, put in the archive as `httpd-webroot.img` -- `extra` takes +# the basename, so the path here is only where `make webroot` writes it and the +# name an operator sees is the file's own. Listed rather than generated by +# mbtdist: the image is not a dataset the install job creates, and nothing in +# the SYSMOD or the RECEIVE plan knows about it. `make package` and `make dist` +# both depend on the target that builds it, because a missing `extra` file is a +# hard error here -- deliberately: an archive quietly missing the webroot would +# look complete and serve nothing on /. +# +# Under build/ rather than dist/ on purpose. The release workflow publishes +# `dist/*` as Release assets, so an image there would show up beside the +# versioned artifacts under a version-less name, duplicating what the archive +# already carries. +extra = ["build/webroot/httpd-webroot.img"] + [distribution.smp] # One FMID per minor release, spent exactly once: it names a functional level, # and a changed one is a new level. Patches on top of it ship as PTFs. 4.0.x diff --git a/samplib/httpwebr b/samplib/httpwebr new file mode 100644 index 0000000..5a32130 --- /dev/null +++ b/samplib/httpwebr @@ -0,0 +1,35 @@ +//HTTPWEBR JOB (ACCT),'ALLOC HTTPD WEBROOT',CLASS=A,MSGCLASS=H, +// MSGLEVEL=(1,1),NOTIFY=&SYSUID +//* +//* HTTPWEBR - allocate the dataset the webroot disk is uploaded to +//* +//* The release archive carries httpd-webroot.img: a formatted UFS370 +//* filesystem holding the pages HTTPD serves from DOCROOT. You upload +//* it with IND$FILE and mount it with UFSD. The installation guide +//* has the whole procedure -- this job is only its first step. +//* +//* RUN THIS BEFORE THE UPLOAD. IND$FILE allocates an existing dataset +//* DISP=SHR and takes the DCB from its label; it invents one only when +//* the dataset is not there. Allocating it here is what pins +//* BLKSIZE=4096, the block size the image was formatted with. UFSD +//* compares the two at MOUNT and refuses a mismatch with: +//* +//* UFSD062E SUPERBLOCK VALIDATION FAILED FOR +//* +//* Primary extent only, on purpose. The filesystem is addressed by +//* block number within the primary extent, so blocks in a secondary +//* would never be reached. SPACE is counted in blocks of 4096 here, +//* which makes 256 blocks the 1 MB the shipped image occupies. MVS +//* rounds the request up to a track boundary; the few blocks that adds +//* sit past the end of the filesystem, because the superblock -- not +//* the dataset -- is what says how large the filesystem is. +//* +//* Change the dataset name, UNIT and VOLUME to suit. The name below +//* is the one the installation guide uses. An image larger than the +//* shipped one needs a matching SPACE: count it in 4096-byte blocks. +//* +//ALLOC EXEC PGM=IEFBR14 +//WEBROOT DD DSN=HTTPD.@VRM@.WEBROOT.UFS,DISP=(NEW,CATLG), +// UNIT=SYSDA,SPACE=(4096,256), +// DCB=(DSORG=PS,RECFM=U,BLKSIZE=4096) +// diff --git a/smp-todo.md b/smp-todo.md index 638d485..0db5839 100644 --- a/smp-todo.md +++ b/smp-todo.md @@ -9,7 +9,7 @@ die Messungen vermerkt. Was ein Betreiber tun muss, steht in Dieses Dokument hält nur noch das, was **für httpd offen** ist. -> *Stand: 2026-08-23, gegen `[distribution]` in `project.toml` abgeglichen.* +> *Stand: 2026-08-24, gegen `[distribution]` in `project.toml` abgeglichen.* > **Die frühere Fassung verwies auf `../SMP-COOKBOOK.md` und > `../SMP-INSTALLATION.md`. Beide Dateien gibt es nicht mehr.** Der Spike liegt @@ -50,7 +50,7 @@ haben diesen Job vor ihrem Tag gefahren, httpd noch nicht — der Kommentar in Die Generalprobe aus einem `-dev`-Baum. Ein Testlauf unter `THTP400`, der abbricht oder halb angewendet wird, belegt genau die ID, die das getaggte 4.0.0 braucht — und das CDS lässt sich hinterher nur mit `UCLIN` bereinigen -(`docs/installation.md` §11). +(`docs/installation.md` §12). Mit mbt ist das eine Zeile: `fmid` in `project.toml` auf `TTST400` setzen, `make package`, installieren — **und die Änderung nie committen**. @@ -60,40 +60,55 @@ man etwas dafür tun muss: `@VRM@` kommt aus der Projektversion, und `4.0.0-dev` ergibt `V4R0M0D`, das getaggte `4.0.0` dagegen `V4R0M0`. Der Test lebt also in `HTTPD.V4R0M0D.*`, das Release in `HTTPD.V4R0M0.*`. -Aufräumen danach: UCLIN-Job aus `docs/installation.md` §11 mit `TTST400`, dann +Aufräumen danach: UCLIN-Job aus `docs/installation.md` §12 mit `TTST400`, dann die Datasets löschen, dann `LIST CDS/ACDS SYSMOD(THTP400)` — das ist zugleich O1. -### O3 — UFS-Webroot: SMP kann das nicht ausliefern - -Eine UFS-Disk ist ein `DSORG=PS`, `RECFM=U`, `BLKSIZE=4096`-Binärimage; SMP4 -kennt dafür keinen Elementtyp (es kennt nur -`++MOD/MAC/SRC/MACUPD/SRCUPD/ZAP`). Sie ist außerdem **Site-Inhalt** — ein -erneutes APPLY dürfte sie nie anfassen. - -Heute steht in `docs/installation.md`, dass ohne UFSD nur die `MOD=`-Routen -laufen und `HTTPD044W` kommt. Das ist ehrlich, aber es ist keine Auslieferung. -Offen: - -- [ ] **U1** Image bauen — `ufsd-utils create webroot.img --size 10M`, - `ufsd-utils cp -r static/ webroot.img:/`, danach `ufsd-utils ls -l` - gegenprüfen (Falle T3) -- [ ] **U2** Hochladen: `ufsd-utils upload webroot.img --dsn HTTPD.WEBROOT` -- [ ] **U3** Mount dokumentieren — `UFSDPRMx` gehört **ufsd**, nicht httpd: - `MOUNT DSN(HTTPD.WEBROOT) PATH(/www) MODE(RO)`, oder dynamisch - `/F UFSD,MOUNT DSN=HTTPD.WEBROOT,PATH=/www,MODE=RO`. Kein `++MACUPD` in - ufsds Parmlib -- [ ] **U4** Reihenfolge: **ufsd zuerst**, Dateisystem gemountet, `/www` - existiert — *dann* httpd. Eine Laufzeitabhängigkeit, die SMP nicht - abbilden kann -- [ ] **U5** `make webroot` + CI-Asset `httpd--webroot.img`, mit - SHA256 statt eines Reproduzierbarkeitsversprechens -- [ ] **U6** **Update-Verhalten festlegen** — `.SAMPLE`-Name oder Existenzprüfung. - Ein `ufsd-utils upload --replace` im Update-Job wäre Datenverlust beim - Betreiber - -`[distribution] extra = [...]` kann eine Datei ins Archiv legen, ohne SMP zu -behelligen — das ist der wahrscheinliche Weg für U5, sobald U6 entschieden ist. +### O3 — UFS-Webroot: entschieden und umgesetzt (Issue #252) + +Die Disk fährt **nicht** über SMP und **nicht** über XMIT, sondern als Datei im +Archiv und per **IND$FILE** aufs Ziel. Beides aus einem strukturellen Grund, +nicht aus Werkzeugmangel: + +- SMP4 kennt für ein `DSORG=PS`/`RECFM=U`-Image keinen Elementtyp + (`++MOD/MAC/SRC/MACUPD/SRCUPD/ZAP` ist die ganze Liste), und es ist + **Site-Inhalt** — ein APPLY dürfte es nie anfassen. +- TSO RECEIVE legt sein Ziel selbst an und weigert sich, in ein bestehendes + Dataset zu mischen: ein Transport nur für die Erstinstallation, für etwas, das + der Betreiber ersetzt. (`xmit370` könnte das Format ohnehin nicht — Verzeichnis + → PDS, `--recfm fb|f`, LRECL 80.) + +Umgesetzt: + +- **`make webroot`** baut `build/webroot/httpd-webroot.img` aus `static/` mit einem + gepinnten `ufsd-utils` (1 MB = 256 Blöcke, `--owner`/`--group` gesetzt, damit + im Artefakt keine Build-Maschinen-Userid steht). `package` und `dist` hängen + davon ab, `[distribution] extra` legt es ins Archiv. +- **`samplib/httpwebr`** allokiert `HTTPD.@VRM@.WEBROOT.UFS` + (`SPACE=(4096,256)`, `RECFM=U BLKSIZE=4096`, nur Primärextent). +- **`docs/installation.md` §9** ist der Betreiberweg: allokieren, IND$FILE, + mounten, prüfen, ersetzen. + +Was dabei gemessen wurde und in der Doku steht: + +| Frage | Befund | +|---|---| +| Blockung | RECFM=U puffert über `__fputc` bis BLKSIZE (libc370 `@@fputc.c:24`) → `BLKSIZE(4096)` schreibt exakte 4096er-Blöcke; 1 MB = 256 ganze Blöcke | +| Optionen | Binär ist Default, RECFM dann `U` (ind_file370 `indparse.c:249`) | +| Vorher allokieren | IND$FILE nimmt ein existierendes Dataset DISP=SHR mitsamt DCB (`indmain.c:190`, `:273`) → Upload ohne jede Option, unabhängig vom IND$FILE-Build | +| Falle | dasselbe DISP=SHR schreibt auch in ein *gemountetes* Dataset → `UNMOUNT PATH=/www` ist ein nummerierter Schritt | +| Fehlerbild | BLKSIZE-Mismatch fällt beim MOUNT auf: `UFSD062E` (ufsd `ufsd#sbl.c:69`) | +| Codepage | keine Entscheidung nötig: `http_send_file()` übersetzt UFS-Dateien fest mit IBM-1047 (`src/httpfile.c:70`), unabhängig von `CODEPAGE=` — genau was `ufsd-utils cp` schreibt. Round-Trip byteweise gemessen, inkl. UTF-8; nur `0x85` und `0xF7` überleben ihn nicht | + +Damit ist auch **U6** (Datenverlust beim Update) erledigt, ohne +`.SAMPLE`-Konstruktion: das Dataset trägt die Version im Namen, kann also die +Platte des Betreibers gar nicht treffen. + +Offen geblieben: + +- [ ] **U5-Rest** SHA256 der Release-Assets — mbt veröffentlicht heute keine + Prüfsummen. Das Image trägt einen Erstellungszeitstempel, ist also nicht + reproduzierbar; die Doku sagt das auch so, statt es zu versprechen. ### O4 — Setup-Job: ausliefern oder Handarbeit lassen? @@ -153,7 +168,7 @@ abgearbeitet oder ersetzt: zurückzunehmen. Der Preis ist, dass die FMID selbst permanent wird: `RESTORE` scheitert, weil akzeptiert, und `REJECT`, weil der ACCEPT die MCS aus dem SMPPTS entfernt. Der Weg zurück ist `UCLIN`, beschrieben in - `docs/installation.md` §11. **Service (PTFs) wird weiterhin nie akzeptiert.** + `docs/installation.md` §12. **Service (PTFs) wird weiterhin nie akzeptiert.** ---