Skip to content

bencz/web-os400

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 

Repository files navigation

WebOS/400 — Developer Guide

image

This document explains the kernel architecture, the boot lifecycle, how to create programs, jobs, libraries, and subsystems, how the message system works, database persistence, and how to develop and run locally.

Contents:

  • Overview and Conventions
  • Kernel Boot and Lifecycle
  • Core (System) and Managers
  • Jobs and Subsystems
  • Libraries and Program/Object Catalog
  • Message System (Message Queue)
  • Persistence (DatabaseManager / SQLite WASM / IndexedDB)
  • Security and Authority (AuthorityManager)
  • Base Program class and Terminal APIs
  • Creating a new Program (step by step)
  • System Auditing (AUDITJRNP, DSPAUDLOG, DSPAUDMON)
  • How to run in development
  • Tips and Troubleshooting

Overview and Conventions

WebOS/400 is a web OS inspired by IBM i/AS/400 with a 100% object‑oriented architecture. Every system element (programs, jobs, subsystems, libraries) is an object derived from SystemObject.

Key directories:

  • src/kernel/core/: kernel core and managers (jobs, subsystems, libraries, messages, authority, DB, etc.).
  • src/apps/system/: system programs (e.g., SIGNON, MAIN, SYSSTATUS, commands and utilities).
  • src/ui/: UI (Terminal, SystemUI, styles, etc.).

Conventions and IDs:

  • Objects use IDs in the form LIB/NAME.TYPE (see SystemObject).
  • Qualified program name: LIB/PGM; unqualified: PGM resolved via job’s current library and library list.
  • System message queues: QSYSOPR, QSYSMSG, QHIST.

Kernel Boot and Lifecycle

File: src/kernel/bootstrap.js

Boot flow:

  1. Instantiate System and call system.initialize().
  2. Expose window.webOS.system = system for console inspection.
  3. Create and initialize SystemUI and Terminal; attach Terminal to #terminal-container.
  4. Start the interactive subsystem QINTER.
  5. Create the initial user job: QSECOFR/QINTER/SIGNON.
  6. Run the SIGNON program on the initial job through ProgramManager.

Boot failures display an error screen with F3/F12 hints.


Core (System) and Managers

File: src/kernel/core/System.js

Kernel components:

  • MessageQueue: message queues and listeners.
  • JobManager: create/end/list jobs.
  • SubsystemManager: create and control subsystems.
  • LibraryManager: libraries and objects.
  • AuthorityManager: users, groups, authority checks.
  • ProgramManager: program registration, cataloging, and execution.
  • DatabaseManager: SQLite persistence (sql.js/WASM) in IndexedDB.

During System.initialize():

  • Initializes DB, MQ, and all managers.
  • Ensures system libraries and subsystems (QSYS, QGPL, QTEMP, etc.; QINTER, QBATCH, QCMN).
  • Sends a “system initialized” message to QSYSOPR.
  • Starts QBATCH and launches the audit job AUDITJRNP in the background (non‑blocking).

System status:

  • getSystemStatus() exposes state, uptime, simulated load, job/subsystem counters, and memory.

Jobs and Subsystems

Files: src/kernel/core/JobManager.js, src/kernel/core/Job.js, src/kernel/core/SubsystemManager.js, src/kernel/core/Subsystem.js

Jobs:

  • ID format: NNNNNN/USER/JOBNAME (e.g., 000001/QSECOFR/SIGNON).
  • Each job has its own message queue (JOB_<id>), status, priority, and call stack.
  • Libraries: currentLibrary and libraryList with helpers (setCurrentLibrary, setLibraryList, addToLibraryList, getLibraryList). Defaults: currentLibrary='QGPL', libraryList=['QTEMP','QGPL','QSYS'].
  • Job start/end events are sent to QSYSMSG (for auditing).

Subsystems:

  • Config: description, maxJobs, autoStart, priority.
  • Each subsystem has a SBS_<name> queue and a state (INACTIVE/ACTIVE).
  • SubsystemManager handles create/start/stop; System.initialize() creates QINTER, QBATCH, QCMN (autoStart=true).

Libraries and Program/Object Catalog

Files: src/kernel/core/LibraryManager.js, src/kernel/core/Library.js, src/kernel/core/ProgramManager.js

  • LibraryManager loads/creates libraries and persists them in DB (libraries).
  • Each Library keeps an object map (addObject, getObject, listObjects).
  • ProgramManager registers in‑memory programs and catalogs them in DB (programs).

Program resolution in ProgramManager.runProgram():

  • Accepts LIB/PGM (qualified) or PGM (unqualified).
  • For unqualified names: searches the job’s current library and *LIBL (see Job helpers).
  • Verifies the DB catalog; requires the program to be loaded in ProgramManager and cataloged.
  • Performs execution authority checks via AuthorityManager.

Registration and cataloging:

  • System programs are imported and registered in registerSystemPrograms().
  • registerProgram(name, programClass, library) enables dynamic registration (adds to a library and catalogs in DB, persisted).

Message System (Message Queue)

File: src/kernel/core/MessageQueue.js

  • Default queues: QSYSOPR, QSYSMSG, QHIST (created at init).
  • sendMessage(queue, msg): assigns id, timestamp, enqueues, logs to history, notifies listeners.
  • receiveMessage(queue, timeout): single‑consumer FIFO.
  • addListener(queue, fn): every listener receives a broadcast fanout of each message.
  • Additional queues are created on demand.

Usage patterns:

  • Jobs and subsystems emit events to QSYSMSG (e.g., JOB_STARTED, SUBSYSTEM_STOPPED).
  • Programs may consume/produce messages on their own or system queues.

Persistence (DatabaseManager)

File: src/kernel/core/DatabaseManager.js

  • Uses sql.js (WASM) loaded via CDN and persists the DB binary in IndexedDB.
  • DB name: webos400 (object store files), file key: webos400.db.
  • Migrations create tables: users, libraries, files, members, records, programs, audit_log.
  • Indexes: unique idx_audit_log_event_id guarantees event de‑duplication.
  • Helpers: execute, query, queryOne, persist.

Note: for existing DBs, light migrations are applied during initialization to ensure new tables and indexes.


Security and Authority (AuthorityManager)

File: src/kernel/core/AuthorityManager.js

  • Loads and persists users in SQLite (keeps an in‑memory cache).
  • Ensures QSECOFR user on init.
  • signOn(user, password), signOff(), getCurrentUser().
  • checkAuthority(user, objectId, authority): validates *EXECUTE/*READ/*CHANGE/*ALL considering user, groups, and special authorities (e.g., *ALLOBJ).
  • ProgramManager.runProgram() uses checkAuthority to validate execution authority.

Base Program class and Terminal APIs

File: src/kernel/core/Program.js

Lifecycle:

  • run(job, terminal, parameters): calls initialize() then execute(); catches errors and calls handleError(); sets returnCode.
  • Subclasses implement async execute().

Kernel APIs inside a Program:

  • Messages: sendMessage(queue, msg), receiveMessage(queue, timeout).
  • Jobs: createJob(user, subsystem, jobName).
  • Auth/User: checkAuthority(), getCurrentUser().
  • System: getSystemStatus().

Terminal APIs exposed via Program:

  • writeToTerminal(row, col, text, attr?, color?).
  • clearTerminal().
  • setCursor(row, col).
  • createInputField(row, col, length, name, value?).
  • waitForInput() (or terminal.waitForSubmit() when available).
  • Default error handling: handleError(error) writes at the last line with highlight.

Note: the Terminal interface is used indirectly by Program (see Program delegating to terminal.writeAt, clearScreen, etc.).


Creating a new Program (step by step)

  1. Create the program file exporting a default class extending Program.

Example (simplified):

// src/apps/system/HELLOWLD.js
import { Program } from '../../kernel/core/Program.js';

export default class HELLOWLD extends Program {
  constructor(system) {
    super('HELLOWLD', 'QGPL', system);
    this.description = 'Sample Hello World';
  }

  async execute() {
    this.clearTerminal();
    this.writeToTerminal(2, 2, 'Hello, WebOS/400!');
    this.setCursor(24, 2);
    await this.waitForInput(); // waits for Enter/Fxx depending on Terminal support
  }
}
  1. Register the program in ProgramManager.

Option A — Dynamic registration at runtime:

// Somewhere after boot (e.g., in an installer program or console)
const { system } = window.webOS;
const { default: HELLOWLD } = await import('./src/apps/system/HELLOWLD.js');
await system.programManager.registerProgram('HELLOWLD', HELLOWLD, 'QGPL');

Option B — Static registration: add the import in registerSystemPrograms() inside ProgramManager.

  • Follow the pattern for SIGNON, MAIN, SYSSTATUS, etc.
  • After registration, the program is added to the library and cataloged in programs (SQLite) automatically.
  1. Running the program
  • Use ProgramManager.runProgram(name, job, terminal, parameters?).
  • Typically a menu program invokes others passing the active job and terminal.
  • For console/dev usage: create a job in QINTER and invoke from a caller program that owns the terminal.

*CURLIB/*LIBL resolution:

  • Calling runProgram('HELLOWLD', ...) without qualification resolves via the job’s current library and library list.
  • You can change currentLibrary and libraryList on the Job as needed (e.g., job.addToLibraryList('MYLIB')).

Authority:

  • ProgramManager validates *EXECUTE for the current user. QSECOFR has full authority by default.

System Auditing

Relevant programs: AUDITJRNP, DSPAUDLOG, DSPAUDMON (in src/apps/system/).

  • AUDITJRNP: batch job started during boot (in System.initialize()), listens to QSYSMSG events and persists to audit_log. De‑duplication by event_id (unique index + in‑memory processed set).
  • DSPAUDLOG: viewer/searcher for persisted audit log (SQLite).
  • DSPAUDMON: real‑time monitor, listening to new queue messages (MQ listener fanout) and displaying live.

MQ semantics applied to auditing:

  • Listeners receive a broadcast fanout of every message on a queue.
  • receiveMessage() delivers each message to a single consumer (FIFO).

How to run in development

Prerequisite: Python 3 available on PATH.

  1. Install dependencies: there are no NPM deps; the project uses sql.js via CDN.
  2. Start a local HTTP server:
npm run start
# or
npm run dev
# or
python3 -m http.server 8080
  1. Open http://localhost:8080 and wait for the SIGNON screen.

At runtime:

  • The system object is accessible at window.webOS.system (useful for console tests).

Persistence:

  • The SQLite DB is saved in the browser’s IndexedDB under key webos400.db. To reset, clear site data in DevTools (Application -> Storage -> Clear site data).

Tips and Troubleshooting

  • Program not found

    • Ensure it is registered in ProgramManager and cataloged in the DB.
    • For unqualified calls, confirm it’s in the job’s *LIBL.
  • Authority error when running a program

    • Ensure the current user has *EXECUTE on the program object (AuthorityManager).
    • QSECOFR has full authority.
  • Messages not reaching the listener

    • Confirm addListener(queue, fn) on the correct queue and that messages are sent with sendMessage.
  • DB “doesn’t persist”

    • After changes via DatabaseManager.execute, call persist() when needed (kernel managers already do at critical points).
  • Terminal/UI

    • Programs output via writeToTerminal and often wait with waitForInput(). The Terminal UI auto‑fits the container and uses an 80x24 fixed‑cell grid.

Quick Reference (key files)

  • Boot: src/kernel/bootstrap.js
  • Kernel: src/kernel/core/System.js
  • Programs: src/kernel/core/Program.js, src/kernel/core/ProgramManager.js
  • Jobs: src/kernel/core/Job.js, src/kernel/core/JobManager.js
  • Subsystems: src/kernel/core/Subsystem.js, src/kernel/core/SubsystemManager.js
  • Libraries: src/kernel/core/Library.js, src/kernel/core/LibraryManager.js
  • Messages: src/kernel/core/MessageQueue.js
  • Authority: src/kernel/core/AuthorityManager.js
  • DB: src/kernel/core/DatabaseManager.js
  • UI: src/ui/components/Terminal.js, src/ui/SystemUI.js

About

A web version of OS/400

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published