---
title: POSIX Print Spooler
description: Job scheduler with signal handling
section: craft
tags: [project, systems-programming]
genre: reference
stability: stable
lastUpdated: 2026-04-19
url: https://fardiniqbal.com/docs/craft/projects/posix-print-spooler
---


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 [#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 [#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 [#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) — `getopt` entry, `sf_init`/`sf_fini` bookends.

## Key features [#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 by `presi_connect_to_printer()`. Pipeline
  members share a process group so signals hit every stage atomically.
* **Signal handling** — `SIGCHLD` reaps dying pipeline children and updates
  job state without blocking the CLI; `SIGSTOP`/`SIGCONT` pause and resume
  jobs by targeting the whole process group; `SIGTERM` drives 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.c` exposes a directed graph of
  `conversion <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`, `disable` target live jobs
  or printers; state transitions fire `sf_*` event hooks for deterministic
  grading and replay.

## What makes it stand out [#what-makes-it-stand-out]

* **Raw POSIX, zero shell.** No `system()`, no wrappers — every process is
  `fork` + `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=yes` runs zero
  leaks, zero leaked descriptors across the Criterion suite.

## Stack [#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                                    |

## Links [#links]

* **Source:** [https://github.com/FardinIqbal/posix-printer-spooler](https://github.com/FardinIqbal/posix-printer-spooler)
