POSIX Print Spooler
Job scheduler with signal handling
A POSIX-compliant print spooler written in C99 against raw system calls —
fork, execvp, pipe, dup2, waitpid, sigaction, Unix domain sockets. Multi-process
job pipelines, a signal-driven scheduler, and a runtime conversion graph that
chains file-type converters into printer endpoints. Zero system() calls.
What it is
A driver CLI, a scheduler, and per-job pipelines that chain converters into
printer endpoints over Unix domain sockets. Jobs traverse
JOB_CREATED -> JOB_RUNNING -> {PAUSED, FINISHED, ABORTED, DELETED};
printers traverse DISABLED -> IDLE -> BUSY. The scheduler matches jobs to
eligible printers by declared file type and resolves multi-stage conversions
via shortest-path search over a directed graph. Built for Stony Brook CSE 320
(Systems Programming, Spring 2025); the presi.h contract is instructor-fixed,
all src/ logic is original.
By the numbers
| Metric | Value |
|---|---|
| Source lines | 1,684 C across 5 files |
| Headers | 9 in include/ |
| Test files | 3 (Criterion framework) |
| Capacity | 32 printers, 64 concurrent jobs |
system() calls | 0 |
| Compiler flags | -Wall -Werror |
Architecture
driver (CLI)
|
v
command_handler -- parses type / printer / conversion / print / pause / resume / cancel / disable / enable
|
v
job_manager -- job table, state machine, pipeline fork/exec, pgid tracking
| ^
| | SIGCHLD, SIGSTOP/SIGCONT, SIGTERM
v |
printer_manager -- printer table, Unix domain socket connect, file-type match
|
v
printer processes (util/printer) converter processes (util/convert)src/cli.c(279 lines) — readline-style command loop.src/command_handler.c(533 lines) — lexing, dispatch, argument validation.src/job_manager.c(579 lines) — job lifecycle, fork pipeline, SIGCHLD reaper.src/printer_manager.c(207 lines) — printer registry, socket wiring.src/main.c(86 lines) —getoptentry,sf_init/sf_finibookends.
Key features
- Process pipelines — each job spawns a master that forks a chain of
converters via
fork()+pipe()+dup2()+execvp(). The final stage writes to a printer fd returned bypresi_connect_to_printer(). Pipeline members share a process group so signals hit every stage atomically. - Signal handling —
SIGCHLDreaps dying pipeline children and updates job state without blocking the CLI;SIGSTOP/SIGCONTpause and resume jobs by targeting the whole process group;SIGTERMdrives graceful shutdown. All handlers are async-signal-safe;sigprocmask()protects shared state across the main loop and the handler. - IPC — Unix domain sockets connect the spooler to printer daemons; pipes carry converted bytes between pipeline stages. No shared memory, no threads.
- Conversion graph —
conversions.cexposes a directed graph ofconversion <from> <to> <cmd>edges. The scheduler runs shortest-path search to synthesize a multi-stage pipeline when no direct converter exists. - Job control —
pause,resume,cancel,disabletarget live jobs or printers; state transitions firesf_*event hooks for deterministic grading and replay.
What makes it stand out
- Raw POSIX, zero shell. No
system(), no wrappers — every process isfork+execvp, every byte moves through explicit pipes or sockets. - Graph-resolved pipelines. Indirect conversions synthesize at runtime via shortest-path search over the converter graph.
- Clean under Valgrind.
--leak-check=full --track-fds=yesruns zero leaks, zero leaked descriptors across the Criterion suite.
Stack
| Layer | Technology |
|---|---|
| Language | C99 (_POSIX_SOURCE, _DEFAULT_SOURCE) |
| Process / IPC | fork, execvp, waitpid, pipe, dup2, setpgid, Unix domain sockets |
| Signals | sigaction, sigprocmask, kill |
| Toolchain | GCC -Wall -Werror, Criterion, Valgrind |