Repository navigation
Expand file tree
/
Copy pathfreebsd-asl-plan.html
More file actions
725 lines (602 loc) · 49.4 KB
/
Copy pathfreebsd-asl-plan.html
File metadata and controls
725 lines (602 loc) · 49.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>FreeBSD ASL + libnotify — porting plan</title>
<style>
:root {
--bg: #fbfbf8;
--fg: #1a1a1a;
--muted: #555;
--accent: #b03000;
--accent2: #0a4d68;
--ok: #1f7a1f;
--warn: #b06800;
--bad: #b00020;
--code-bg: #f0ece4;
--rule: #d6cfc0;
--card: #fff;
}
html { -webkit-text-size-adjust: 100%; }
body { margin: 0 auto; max-width: 980px; padding: 2.5rem 1.5rem 6rem;
font: 16px/1.55 -apple-system, BlinkMacSystemFont, "SF Pro Text", system-ui, sans-serif;
color: var(--fg); background: var(--bg); }
h1 { font-size: 2rem; line-height: 1.2; margin: 0 0 .25rem; }
h2 { font-size: 1.4rem; margin: 2.5rem 0 .75rem; padding-bottom: .25rem; border-bottom: 2px solid var(--rule); }
h3 { font-size: 1.15rem; margin: 1.75rem 0 .5rem; color: var(--accent2); }
h4 { margin: 1.25rem 0 .35rem; }
.subtitle { color: var(--muted); font-size: 1.05rem; margin: 0 0 2rem; }
code, pre, kbd { font-family: "SF Mono", Menlo, Consolas, monospace; }
code { background: var(--code-bg); padding: 1px 5px; border-radius: 3px; font-size: .9em; }
pre { background: var(--code-bg); padding: .85rem 1rem; border-radius: 6px;
overflow-x: auto; font-size: .82rem; line-height: 1.45;
border-left: 3px solid var(--accent2); }
pre code { background: none; padding: 0; }
pre.shell { border-left-color: var(--ok); }
pre.plist { border-left-color: var(--accent); }
pre.objc { border-left-color: #6a3c8c; }
a { color: var(--accent2); }
a:hover { color: var(--accent); }
.tldr { background: var(--card); border: 1px solid var(--rule); border-left: 4px solid var(--accent2);
padding: 1rem 1.25rem; border-radius: 6px; margin-bottom: 2rem; }
.tldr h3 { margin-top: 0; color: var(--accent2); }
.pill { display: inline-block; font-size: .72rem; padding: 1px 8px; border-radius: 999px;
background: #eee; color: #333; margin-left: .35rem; vertical-align: middle;
font-weight: 600; letter-spacing: .02em; }
.pill.ok { background: #d8efd8; color: var(--ok); }
.pill.warn { background: #f6e4cb; color: var(--warn); }
.pill.bad { background: #f5d0d6; color: var(--bad); }
.pill.info { background: #d6e6f3; color: var(--accent2); }
.phase { background: var(--card); border: 1px solid var(--rule); border-radius: 6px;
padding: 1.2rem 1.4rem; margin: 1rem 0; }
.phase h3 { margin-top: 0; }
.phase.todo { border-left: 4px solid var(--accent2); }
.phase.defer { border-left: 4px solid var(--warn); }
table { width: 100%; border-collapse: collapse; font-size: .92rem; margin: 1rem 0; }
th, td { text-align: left; padding: .55rem .7rem; vertical-align: top;
border-bottom: 1px solid var(--rule); }
th { background: var(--code-bg); font-weight: 600; }
td code { font-size: .85em; }
.src { color: var(--muted); font-size: .82rem; }
.footnote { font-size: .85rem; color: var(--muted); margin-top: .5rem; }
hr { border: 0; border-top: 1px solid var(--rule); margin: 2.5rem 0; }
.toc { background: var(--card); border: 1px solid var(--rule); border-radius: 6px;
padding: 1rem 1.2rem; margin: 1.5rem 0 2rem; }
.toc h3 { margin-top: 0; }
.toc ol { margin: 0; padding-left: 1.4rem; }
.toc li { margin: .25rem 0; }
</style>
</head>
<body>
<h1>FreeBSD ASL + libnotify — porting plan</h1>
<p class="subtitle">Port Apple's ASL (Apple System Log) + libnotify+notifyd as a single cohesive block, replacing the removed FreeBSD-syslogd and unblocking the downstream Apple-service daemon ports (configd, IPConfiguration, PowerManagement, DiskArbitration, IOKitUser). <strong>Status: draft</strong>; synthesized from five parallel research agents on 2026-05-16. Supersedes the earlier v0 plan (which assumed AF_UNIX / GNUstep Distributed Objects substrate — wrong now that Mach IPC + libxpc + libdispatch + libCoreFoundation are all working).</p>
<p><span class="pill info">Filed under: <code>freebsd-launchd-mach</code> (v2) effort</span> <span class="pill ok">SHIPPED</span></p>
<div class="tldr">
<h3>Revision 2026-05-23</h3>
<p>Confirmed Mach-IPC track. ASL implementation has <strong>SHIPPED</strong> in the <code>freebsd-launchd-mach</code> repo (<code>src/syslog/</code>); Phase J1 (<code>libsystem_asl</code>) and Phase J2 (<code>syslogd</code> / <code>aslmanager</code> / <code>syslog</code> CLI) build steps live in <code>build.sh</code>. CI marker <code>SYSLOG-RUN-OK</code> is green. The original phased plan below remains the canonical reference for what was built and why; sections describing future work now describe shipped work.</p>
</div>
<div class="tldr">
<h3>TL;DR</h3>
<ul>
<li><strong>Port ASL + libnotify+notifyd together</strong>, not separately. libnotify has <strong>347 <code>notify_*</code> call sites across 6 downstream daemons</strong> (ASL=52, IOKitUser=56, configd=60, IPConfiguration=7, PowerManagement=154, DiskArbitration=6); stubbing scales badly because configd/pmconfigd are event-driven and would run permanently broken with stubs.</li>
<li><strong>FreeBSD-syslogd was removed at commit 88694f0</strong> — the runtime ISO has no syslog at all right now. ASL restores logging service; it is no longer a "deferred upgrade path."</li>
<li><strong>Transparent FreeBSD-<code>syslog(3)</code> interop is achievable</strong> with three small patches to Apple syslogd: add a <code>bsd_in.c</code> input module that listens on <code>/var/run/log</code>, add an RFC 5424 branch to <code>asl_syslog_input_convert()</code> (FreeBSD libc emits RFC 5424; Apple syslogd parses RFC 3164), and add a launchd <code>Sockets</code> entry. Zero changes to FreeBSD's getty / login.</li>
<li><strong>Kernel messages</strong> already flow naturally: kernel <code>printf(9)</code> / <code>log(9)</code> → kernel msgbuf → <code>/dev/klog</code> → Apple syslogd's <code>klog_in.c</code> module (works on FreeBSD unmodified) → ASL store. Same path covers FreeBSD core + mach.ko + every other kld.</li>
<li><strong>Cross-cutting stubs</strong> needed alongside: <code>os/log.h</code> + <code>os/log_private.h</code> + <code>os/assumes.h</code> + <code>os/activity.h</code> + <code>os/transaction_private.h</code> header pack (~700 lines, required to compile both ASL and IOKitUser later); <code>membership.h</code> stub (~20 lines, ASL only); <code>configuration_profile.h</code> stub (~10 lines, ASL only).</li>
<li><strong>Use FreeBSD's <code>libbsm</code></strong> as-is — FreeBSD base ships <code><bsm/libbsm.h></code> with identical <code>audit_token_to_au32</code>/<code>audit_token_to_pid</code> signatures (<code>contrib/openbsm/bsm/libbsm.h:1257-1294</code>).</li>
<li><strong>Total scope: ~7-9 weeks</strong> for the complete ASL stack (libasl + libnotify + notifyd + syslogd + aslmanager + syslog(1) CLI). Breakdown: J1 libsystem_asl + libnotify (1-1.5 wk), J2 syslogd + notifyd Mach RX + mach.ko fileport ops (2.5-3 wk), J3 bsd_in.c + /dev/klog + /etc/syslog.conf (1 wk), J4 aslmanager + validation + hwregd handoff (2-3 wk), J5 syslog(1) CLI (1-2 wk).</li>
</ul>
</div>
<div class="toc">
<h3>Contents</h3>
<ol>
<li><a href="#motivation">Motivation — current state + why ASL is the path of least resistance</a></li>
<li><a href="#scope">Scope of port</a></li>
<li><a href="#source">Apple ASL source layout</a></li>
<li><a href="#architecture">syslogd architecture (input modules, output modules, ASL DB)</a></li>
<li><a href="#notify">Why libnotify+notifyd ship alongside ASL</a></li>
<li><a href="#interop">FreeBSD <code>syslog(3)</code> interop strategy</a></li>
<li><a href="#apple-alternatives">6.5 Would switching to Apple utilities drop the bsd_in.c workaround?</a></li>
<li><a href="#install">Install layout</a></li>
<li><a href="#stubs">Stubs needed</a></li>
<li><a href="#consumer-guide">Consumer guide — how to use ASL from C / Objective-C</a></li>
<li><a href="#phases">Phased delivery (J1-J4)</a></li>
<li><a href="#smoke">CI smoke markers</a></li>
<li><a href="#launchd-log">launchd <code>log.c</code> interaction</a></li>
<li><a href="#open-questions">Open questions</a></li>
<li><a href="#sources">Source citations</a></li>
</ol>
</div>
<h2 id="motivation">1. Motivation</h2>
<p>The runtime ISO currently ships <strong>no syslog daemon at all</strong>. FreeBSD-syslogd was removed at commit <code>88694f0</code> (2026-05-16) alongside FreeBSD-rc, FreeBSD-devd, and several other base packages we no longer need under PID-1 launchd. Apps that call <code>syslog(3)</code> via libc (getty, login, and a small handful of FreeBSD-base utilities) write to <code>/var/run/log</code> — which doesn't exist — and the messages silently drop. Kernel printfs accumulate in the kernel msgbuf and never make it to any persistent log.</p>
<p>This is fine for boot-to-login validation (no boot-critical decision depends on logs), but blocks essentially every next step. <strong>hwregd</strong> needs to log events for operational visibility. <strong>configd</strong>, <strong>IPConfiguration</strong>, <strong>mDNSResponder</strong>, <strong>PowerManagement</strong> — every Apple-source service daemon — calls <code>asl_log()</code> natively in its source. We can either:</p>
<ol>
<li><strong>Port Apple's ASL stack</strong> as the structured replacement, which is what every Apple-source daemon already writes for, OR</li>
<li><strong>Reinstall FreeBSD-syslogd</strong>, which works for libc <code>syslog(3)</code> consumers but leaves the Apple-source daemons either calling no-op stubs or shelling out — a permanent two-track logging architecture.</li>
</ol>
<p>Option 2 is the wrong end state; Apple's daemons assume ASL and we'd be undoing this later anyway. Option 1 — port ASL once, correctly — is the path the project's <code>scope_split</code> memory already commits to ("Apple source only for system services: Mach/launchd/configd/notifyd/asl/libdispatch/libxpc/liblaunch"). ASL is in scope.</p>
<h2 id="scope">2. Scope of port</h2>
<table>
<thead><tr><th>Component</th><th>Source location</th><th>Phase</th><th>Notes</th></tr></thead>
<tbody>
<tr><td><code>libsystem_asl</code> (client lib)</td><td><code>syslog/libsystem_asl.tproj/</code></td><td>J1</td><td>~14.6k LoC. Public <code>asl.h</code>, internal subprojects. Every Apple daemon links this.</td></tr>
<tr><td><code>aslcommon</code> (static lib shared by daemon + client)</td><td><code>syslog/aslcommon/</code></td><td>J1</td><td>~4.5k LoC. <code>asl_ipc.defs</code> MIG (subsystem 114), <code>asl_memory.c</code>, <code>asl_common.c</code>.</td></tr>
<tr><td><code>syslogd</code> daemon</td><td><code>syslog/syslogd.tproj/</code></td><td>J2-J3</td><td>~8.4k LoC. Mach RX loop + input/output modules + ASL store driver.</td></tr>
<tr><td><strong>New: <code>bsd_in.c</code> module</strong></td><td>NEW under <code>syslogd.tproj/</code></td><td>J3</td><td>~150 LoC. Listens on <code>/var/run/log</code> (AF_UNIX SOCK_DGRAM) so FreeBSD libc <code>syslog(3)</code> consumers reach ASL transparently.</td></tr>
<tr><td><strong>RFC 5424 branch</strong> in <code>asl_syslog_input_convert()</code></td><td><code>syslogd.tproj/daemon.c:1057</code></td><td>J3</td><td>~50 LoC. FreeBSD libc emits RFC 5424 (<code><pri>1 timestamp ...</code>); Apple parses only RFC 3164.</td></tr>
<tr><td><code>libnotify</code> client lib</td><td><code>Libnotify/</code> (top-level + headers)</td><td>J1</td><td>~7.7k LoC. <code>notify_register_*</code>, <code>notify_post</code>, <code>notify_check</code>, etc.</td></tr>
<tr><td><code>notifyd</code> daemon</td><td><code>Libnotify/notifyd/</code></td><td>J2</td><td>~5.1k LoC. Mach-backed pub/sub server. Required because configd/pmconfigd hard-depend on real notify behavior.</td></tr>
<tr><td><code>os/*</code> header stubs</td><td>NEW under <code>freebsd-shims/</code></td><td>J1</td><td>~700 lines. <code>os_log</code>/<code>os_assumes</code>/<code>os_activity</code>/<code>os_transaction_private</code> macro to printf/syslog/no-op. Required to compile.</td></tr>
<tr><td><code>membership.h</code> stub</td><td>NEW under <code>freebsd-shims/</code></td><td>J1</td><td>~20 lines. <code>mbr_uid_to_uuid</code> → KERN_FAILURE. ASL store ACL groups degrade to uid/gid.</td></tr>
<tr><td><code>configuration_profile.h</code> stub</td><td>NEW under <code>freebsd-shims/</code></td><td>J1</td><td>~10 lines. MDM profile overrides — irrelevant on FreeBSD; returns NULL.</td></tr>
<tr><td><code>aslmanager</code> log-rotation daemon</td><td><code>syslog/aslmanager.tproj/</code></td><td>J4</td><td>~2k LoC. Without it <code>/var/log/asl/</code> grows unbounded (syslogd self-rotates current files but never archives or deletes). Real disk-fillup consequence — not optional for a shipping system.</td></tr>
<tr><td><code>syslog(1)</code> CLI</td><td><code>syslog/util.tproj/</code></td><td>J5</td><td>~2.9k LoC. Required for structured queries against the binary <code>/var/log/asl/*.asl</code> database (operators have no shell-side ASL read access without it). Send-side covered by FreeBSD's existing <code>logger(1)</code> + our bsd_in.c, but read-side is syslog(1)-only.</td></tr>
<tr><td><code>newsyslog</code></td><td><code>syslog/newsyslog/</code></td><td>SKIP</td><td>FreeBSD base already has working <code>newsyslog</code>; no need to port Apple's variant.</td></tr>
</tbody>
</table>
<h2 id="source">3. Apple ASL source layout</h2>
<p>Vendored locally at <code>/Users/jmaloney/Documents/launchd/syslog/</code>. License: APSL 2.0. ~32k LoC total C.</p>
<p>Six Xcode targets in <code>syslog.xcodeproj/project.pbxproj</code>:</p>
<pre><code>aslcommon (static lib) libaslcommon.a
└ asl_ipc.defs (MIG subsystem 114), asl_memory.c, asl_common.c
libsystem_asl (shared lib) libsystem_asl.dylib
└ asl.c (2064), asl_msg.c (3295), asl_file.c (2653),
asl_core.c (1164), asl_store.c (1155), asl_legacy1.c (807),
asl_string.c (747), asl_client.c (614), asl_msg_list.c (587),
asl_object.c (426), asl_util.c (342), asl_fd.c (328),
syslog.c (288 — BSD syslog(3) shim that wraps to ASL),
asl_mt_shim.c (100)
syslogd (tool) /usr/sbin/syslogd
└ syslogd.c (712), daemon.c (1426), dbserver.c (1648),
asl_action.c (2457), bsd_out.c (811), klog_in.c (141),
udp_in.c (222), remote.c (935)
links: libaslcommon.a, libbsm.dylib
aslmanager (tool) /usr/libexec/aslmanager
util (tool) /usr/bin/syslog
newsyslog (tool) skip — FreeBSD base has its own
</code></pre>
<p>Dependency DAG: <code>aslcommon → libsystem_asl → {syslogd, aslmanager, util}</code>. <code>newsyslog</code> is standalone. <strong>No Security.framework, IOKit, CoreFoundation, or libsystem_info usage anywhere</strong> (verified grep).</p>
<h2 id="architecture">4. syslogd architecture</h2>
<h3>Input modules (registered at <code>syslogd.c:224-229</code>)</h3>
<table>
<thead><tr><th>Module</th><th>Source</th><th>Wire</th><th>Status on FreeBSD</th></tr></thead>
<tbody>
<tr><td><code>m_asl</code></td><td>Mach IPC via <code>com.apple.system.logger</code> (launchd MachServices port)</td><td>MIG <code>_asl_server_message</code></td><td>Works as-is once liblaunch returns the Mach port</td></tr>
<tr><td><code>m_klog_in</code></td><td><code>klog_in.c</code> reads <code>/dev/klog</code> via dispatch READ source</td><td>Plain <code>open(O_RDONLY|O_NONBLOCK)</code></td><td>Works on FreeBSD unmodified — <code>/dev/klog</code> exists, same single-reader semantic</td></tr>
<tr><td><code>m_udp_in</code></td><td><code>udp_in.c</code>, network RFC 3164 from UDP 514</td><td>SOCK_DGRAM from launchd <code>Sockets</code></td><td>Disabled by default; enable per-plist if remote syslog needed</td></tr>
<tr><td><code>m_remote</code></td><td><code>remote.c</code>, AF_UNIX SOCK_STREAM control socket</td><td>For <code>syslog -w</code> watchers</td><td>iOS-targeted; skippable for v1</td></tr>
<tr><td><strong>m_bsd_in (NEW)</strong></td><td>NEW <code>bsd_in.c</code> modeled on <code>klog_in.c</code></td><td>AF_UNIX SOCK_DGRAM on <code>/var/run/log</code></td><td>Required for FreeBSD getty/login interop</td></tr>
</tbody>
</table>
<h3>Output modules (in-process, linked at build time)</h3>
<ul>
<li><strong>m_asl</strong> (<code>asl_action.c</code>) — writes ASL binary database to <code>/var/log/asl/*.asl</code> via <code>asl_store.c</code> / <code>asl_file.c</code>. Self-rotates current files.</li>
<li><strong>m_bsd_out</strong> (<code>bsd_out.c</code>) — reads <code>/etc/syslog.conf</code> (native BSD format), writes to <code>/var/log/system.log</code>, <code>/var/log/messages</code>, etc. via the standard facility.priority routing.</li>
</ul>
<h2 id="notify">5. Why libnotify+notifyd ship alongside ASL</h2>
<p>The agent audit found <strong>347 <code>notify_*</code> call sites across 6 downstream Apple daemons</strong>:</p>
<table>
<thead><tr><th>Daemon</th><th><code>notify_*</code> calls</th><th>Files</th><th>Behavior if stubbed</th></tr></thead>
<tbody>
<tr><td>ASL (syslogd + libsystem_asl + aslmanager + util)</td><td>52</td><td>9</td><td>Loses <code>syslog -w</code>, <code>syslog -c</code>, asl.conf <code>=notify</code>. Logging itself fine.</td></tr>
<tr><td>IOKitUser (pwr_mgt + ps subprojects)</td><td>56</td><td>11</td><td>IOPMAssertion / IOPS notifications broken.</td></tr>
<tr><td>configd</td><td>60</td><td>14</td><td><strong>Permanently broken</strong>. Event-driven; network-change/host-name-change/ip-plugin-recompute all hang on notify wakeups.</td></tr>
<tr><td>IPConfiguration</td><td>7</td><td>1</td><td>Lease renewal + interface state-change dropped.</td></tr>
<tr><td>PowerManagement (pmconfigd)</td><td>154</td><td>17</td><td><strong>Permanently broken</strong>. Sleep/wake/lid/battery-thresh all hang.</td></tr>
<tr><td>DiskArbitration</td><td>6</td><td>2</td><td>Disk arrival/eject callbacks broken.</td></tr>
<tr><td><strong>Total</strong></td><td><strong>335</strong></td><td><strong>54</strong></td><td></td></tr>
</tbody>
</table>
<p>For ASL alone, stubbing is plausible (~80 lines across 9 files). But the moment configd or pmconfigd enters scope — and both are in our commit-to-port list — the stub story collapses. libnotify itself has only 3 unported deps, all already on our roadmap. Cost: ~12.8k LoC, ~1.5-2 weeks port effort. Versus stubbing across 6 daemons: ~480 lines of degraded-mode shim, configd + pmconfigd unusable. The leverage ratio (one ~12.8k-LoC port unblocks 5 daemon ports cleanly) makes libnotify the highest-priority cross-cutting dep.</p>
<h2 id="interop">6. FreeBSD <code>syslog(3)</code> interop strategy</h2>
<p>Today's broken chain:</p>
<pre><code>FreeBSD getty/login
→ libc syslog(3) (lib/libc/gen/syslog.c)
→ connect("/var/run/log", AF_UNIX, SOCK_DGRAM)
→ ECONNREFUSED (no listener) → silent drop
</code></pre>
<p>Target chain:</p>
<pre><code>FreeBSD getty/login
→ libc syslog(3)
→ sendto("/var/run/log", AF_UNIX, SOCK_DGRAM, RFC-5424-msg)
→ Apple syslogd's NEW bsd_in.c module
→ asl_input_parse() with patched RFC 5424 branch
→ ASL store + /etc/syslog.conf routing to /var/log/messages
</code></pre>
<p>Three concrete patches required (all in <code>syslogd.tproj/</code>):</p>
<ol>
<li><strong>New <code>bsd_in.c</code></strong> modeled on <code>klog_in.c</code>. Get the listening fd from launchd via <code>LAUNCH_JOBKEY_SOCKETS</code>. Per-dgram <code>recv()</code> → <code>asl_input_parse(buf, len, NULL, SOURCE_BSD_SOCKET)</code>. Register <code>m_bsd_in</code> in <code>syslogd.c:215-229</code>.</li>
<li><strong>RFC 5424 branch</strong> in <code>asl_syslog_input_convert()</code> (<code>daemon.c:1057</code>). FreeBSD libc emits <code><pri>1 YYYY-MM-DDTHH:MM:SS ...</code>; Apple's existing parser hard-checks for the RFC 3164 timestamp <code>Mmm dd HH:MM:SS</code>. Add a branch: if next bytes after <code><pri></code> are <code>1 </code>, parse RFC 5424.</li>
<li><strong>launchd <code>Sockets</code> entry</strong> in the syslogd plist:
<pre class="plist"><code><key>Sockets</key>
<dict>
<key>BSDSystemLogger</key>
<dict>
<key>SockPathName</key><string>/var/run/log</string>
<key>SockType</key><string>dgram</string>
<key>SockPathMode</key><integer>0666</integer>
</dict>
</dict></code></pre>
</li>
</ol>
<p>Kernel log ingest works as-is: <code>klog_in.c</code> opens <code>/dev/klog</code> via plain <code>open(O_RDONLY|O_NONBLOCK)</code>, FreeBSD's <code>/dev/klog</code> from <code>sys/kern/subr_log.c</code> has the same single-reader semantic. <strong>No patches needed</strong>. <code>/etc/syslog.conf</code> compat is already in Apple syslogd via <code>bsd_out_init()</code> at <code>bsd_out.c:732</code> — also no patches.</p>
<h3 id="apple-alternatives">6.5 Would switching to Apple's utilities drop the bsd_in.c workaround?</h3>
<p>Short answer: <strong>partially, and not worth chasing</strong>. Below is the explicit matrix.</p>
<p>Two paths a userland program can reach ASL:</p>
<ol>
<li><strong>Apple-utility path</strong>: program links <code>libsystem_asl.so</code>, calls <code>asl_log()</code> directly OR calls <code>syslog(3)</code> which is intercepted by the libsystem_asl shim (<code>libsystem_asl.tproj/src/syslog.c</code>, 288 lines) and routed to syslogd over Mach IPC. <strong>No bsd_in.c involved.</strong></li>
<li><strong>FreeBSD-utility path</strong>: program links FreeBSD libc only, calls <code>syslog(3)</code> which writes RFC 5424 to <code>/var/run/log</code> AF_UNIX SOCK_DGRAM. <strong>bsd_in.c required</strong> to listen on that socket and route into ASL.</li>
</ol>
<p>So bsd_in.c is the catch-all for path 2. Porting an Apple utility moves <em>that one binary</em> to path 1, but bsd_in.c stays mandatory for every other FreeBSD-base utility still on path 2.</p>
<h4>Per-utility matrix</h4>
<table>
<thead>
<tr>
<th>FreeBSD-base utility</th>
<th>In our rootfs?</th>
<th>Apple equivalent</th>
<th>Apple version uses asl directly?</th>
<th>Porting drops bsd_in.c for this consumer?</th>
<th>Worth porting?</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>/usr/libexec/getty</code></td>
<td>Yes (FreeBSD-runtime)</td>
<td><code>system_cmds/getty.tproj/</code></td>
<td>Yes (via libsystem_asl syslog shim + native asl calls)</td>
<td>Yes (for getty)</td>
<td><span class="pill bad">No</span> — Apple version has Darwin audit hooks + asl-direct integration we'd strip; result functionally equivalent to FreeBSD's. bsd_in.c covers transparently.</td>
</tr>
<tr>
<td><code>/usr/bin/login</code></td>
<td>Yes (FreeBSD-runtime)</td>
<td><code>system_cmds/login.tproj/</code></td>
<td>Yes (asl + audit session)</td>
<td>Yes (for login)</td>
<td><span class="pill bad">No</span> — Same logic as getty. Apple's adds OpenDirectory hooks we don't have; strip-and-port gives near-identical behavior.</td>
</tr>
<tr>
<td><code>/usr/bin/su</code></td>
<td>Yes (FreeBSD-runtime)</td>
<td><code>system_cmds/su.tproj/</code></td>
<td>Yes (asl + audit)</td>
<td>Yes (for su)</td>
<td><span class="pill bad">No</span> — Same reasoning.</td>
</tr>
<tr>
<td><code>/usr/bin/passwd</code></td>
<td>Yes (FreeBSD-runtime)</td>
<td><code>system_cmds/passwd.tproj/</code></td>
<td>Yes</td>
<td>Yes (for passwd)</td>
<td><span class="pill bad">No</span> — Same reasoning.</td>
</tr>
<tr>
<td><code>/usr/sbin/cron</code></td>
<td>Optional (not in minimal rootfs)</td>
<td><code>cron</code> (Apple ships BSD cron with patches)</td>
<td>Partial — calls syslog(3), not asl directly</td>
<td>No real win — even Apple cron uses syslog(3)</td>
<td><span class="pill bad">No</span> — Both versions go through bsd_in.c anyway.</td>
</tr>
<tr>
<td><code>/usr/libexec/atrun</code></td>
<td>Optional</td>
<td>Same as cron — BSD inheritance</td>
<td>No</td>
<td>No</td>
<td><span class="pill bad">No</span></td>
</tr>
<tr>
<td><code>/usr/sbin/sshd</code></td>
<td>No (FreeBSD-ssh removed at 88694f0)</td>
<td>Apple openssh-portable fork (heavily patched)</td>
<td>Yes (asl + audit)</td>
<td>Yes (for sshd when it returns)</td>
<td><span class="pill warn">Maybe later</span> — when sshd comes back, the Apple version brings session-context logging worth having. Independent of this plan.</td>
</tr>
<tr>
<td><code>/sbin/dhclient</code></td>
<td>No (removed at 88694f0; will be replaced by IPConfiguration)</td>
<td>N/A — IPConfiguration is the Apple equivalent</td>
<td>Yes (asl direct)</td>
<td>Yes (IPConfiguration won't use syslog(3))</td>
<td><span class="pill ok">Yes — separate plan</span></td>
</tr>
<tr>
<td><code>/usr/sbin/newsyslog</code></td>
<td>Yes (FreeBSD-runtime)</td>
<td><code>syslog/newsyslog/</code> (Apple variant)</td>
<td>No (BSD-style)</td>
<td>No</td>
<td><span class="pill bad">No</span> — FreeBSD's works; Apple's variant in our vendored syslog repo is intentionally skipped per the J0 plan.</td>
</tr>
<tr>
<td><code>/usr/sbin/syslogd</code></td>
<td>No (removed at 88694f0)</td>
<td>This plan</td>
<td>N/A — IS the daemon</td>
<td>N/A</td>
<td><span class="pill ok">Yes — this plan</span></td>
</tr>
<tr>
<td><code>/usr/sbin/powerd</code></td>
<td>Not in our build path</td>
<td><code>PowerManagement/pmconfigd/</code></td>
<td>Yes (asl + notify)</td>
<td>Yes (when pmconfigd lands)</td>
<td><span class="pill warn">Eventually</span> — covered by IOKit/PowerManagement plan downstream.</td>
</tr>
<tr>
<td><code>/usr/sbin/ntpd</code></td>
<td>Not in our build path</td>
<td>Apple ntp fork (deprecated on macOS in favor of timed)</td>
<td>Some — calls asl in patches</td>
<td>Partial</td>
<td><span class="pill bad">No</span> — Apple themselves abandoned ntpd; if time sync becomes a need, port Apple's timed or ship freebsd-base ntpd through bsd_in.c.</td>
</tr>
<tr>
<td>Future ports-tree apps using syslog(3)</td>
<td>Whatever the user installs</td>
<td>N/A — third-party</td>
<td>No</td>
<td>No (can't patch user-installed pkgs)</td>
<td><span class="pill ok">bsd_in.c is the only answer</span></td>
</tr>
</tbody>
</table>
<h4>Conclusion</h4>
<p>Even if we ported every Apple system_cmds utility that has a syslog-using counterpart (getty / login / su / passwd / atrun), <strong>bsd_in.c would still be required</strong> for:</p>
<ul>
<li>Any future ports-tree pkg the user installs (postgres, nginx, postfix, etc.) — third-party C/C++ code calling libc <code>syslog(3)</code> universally</li>
<li>FreeBSD utilities we keep because their Apple equivalent isn't worth porting (cron, atrun, newsyslog, ntpd)</li>
<li>FreeBSD utilities we haven't yet replaced (because there are always more)</li>
</ul>
<p>And per the earlier audit (<a href="freebsd-apple-userland-cmds-plan.html">apple-userland-cmds plan</a> and the IOKit-plan synthesis), <strong>Apple's userland utilities that talk to the kernel (getty/login/ifconfig/route/netstat/ping/ps/top/mount/etc.) aren't worth porting</strong>: their value over FreeBSD's is near-zero after stripping Darwin-specific kernel-ABI ties (audit sessions, asl-direct integration, OpenDirectory hooks), all of which we don't have anyway. <strong>bsd_in.c is the leverage move</strong>: ~200 LoC covers every present and future FreeBSD <code>syslog(3)</code> consumer transparently.</p>
<p>The only Apple utilities worth porting from a logging-perspective alone are those that talk to <em>Apple services</em> (launchctl, scutil, networksetup, defaults, log, syslog(1) itself) — they have no FreeBSD equivalent and their value compounds beyond just logging.</p>
<h2 id="install">7. Install layout</h2>
<table>
<thead><tr><th>Artifact</th><th>Path</th></tr></thead>
<tbody>
<tr><td><code>libsystem_asl.so</code></td><td><code>/usr/lib/system/libsystem_asl.so</code> + sonname symlink</td></tr>
<tr><td><code>libnotify.so</code> (client lib)</td><td><code>/usr/lib/system/libnotify.so</code> + sonname symlink</td></tr>
<tr><td>Public headers (<code>asl.h</code>, <code>notify.h</code>, <code>notify_keys.h</code>)</td><td><code>/usr/include/</code></td></tr>
<tr><td>Private headers (<code>asl_private.h</code>, etc.)</td><td><code>/usr/include/asl/</code></td></tr>
<tr><td><code>syslogd</code> binary</td><td><code>/usr/sbin/syslogd</code></td></tr>
<tr><td><code>notifyd</code> binary</td><td><code>/usr/sbin/notifyd</code></td></tr>
<tr><td><code>aslmanager</code> binary (J5)</td><td><code>/usr/libexec/aslmanager</code></td></tr>
<tr><td><code>syslog(1)</code> CLI (J5)</td><td><code>/usr/bin/syslog</code></td></tr>
<tr><td>launchd plists</td><td><code>/System/Library/LaunchDaemons/com.apple.syslogd.plist</code>, <code>com.apple.notifyd.plist</code></td></tr>
<tr><td>Default <code>asl.conf</code></td><td><code>/etc/asl.conf</code> + drop-in dir <code>/etc/asl/</code></td></tr>
<tr><td>ASL DB</td><td><code>/var/log/asl/*.asl</code></td></tr>
<tr><td>BSD text log</td><td><code>/var/log/system.log</code>, <code>/var/log/messages</code>, etc.</td></tr>
</tbody>
</table>
<h2 id="stubs">8. Stubs needed</h2>
<ul>
<li><strong><code>os/*</code> header pack</strong> (~700 lines) under <code>src/freebsd-shims/os/</code>. <code>os/log.h</code>, <code>os/log_private.h</code>, <code>os/assumes.h</code>, <code>os/activity.h</code>, <code>os/transaction_private.h</code>. Macro-expand <code>os_log()</code> family to <code>fprintf(stderr, ...)</code> initially; re-expand to <code>asl_log()</code> once ASL is up. Required to compile both ASL and IOKitUser (708 <code>os_log*</code> call sites tree-wide).</li>
<li><strong><code>membership.h</code> stub</strong> (~20 lines). <code>mbr_uid_to_uuid()</code> returns <code>KERN_FAILURE</code>. ASL store ACL groups degrade to plain uid/gid checks.</li>
<li><strong><code>configuration_profile.h</code> stub</strong> (~10 lines). <code>configuration_profile_copy_property_list()</code> returns NULL. MDM-profile overrides irrelevant on FreeBSD.</li>
<li><strong>FreeBSD-native <code>libbsm</code></strong> — base ships identical signatures via <code>contrib/openbsm/bsm/libbsm.h:1257-1294</code>. Skip the Apple version.</li>
<li><strong>Sandbox profile</strong> (<code>syslogd.sb</code>) — skip; no Apple sandbox on FreeBSD.</li>
</ul>
<h2 id="consumer-guide">9. Consumer guide — how to use ASL from C / Objective-C</h2>
<p>This section is for application developers writing new code (or porting existing code) that wants to log via ASL after this plan ships.</p>
<h3>9.1 C — direct ASL API</h3>
<p><strong>Header</strong>: <code><asl.h></code> (lives at <code>/usr/include/asl.h</code> after J1).
<br><strong>Link</strong>: <code>-lsystem_asl</code> (the soname is <code>libsystem_asl.so.1</code> under <code>/usr/lib/system/</code>; rpath baked at link time per <code>install_layout_policies</code>).</p>
<h4>Minimal: fire-and-forget messages</h4>
<pre><code>#include <asl.h>
int main(void)
{
asl_log(NULL, NULL, ASL_LEVEL_NOTICE,
"hello from %s pid=%d", "myapp", (int)getpid());
return 0;
}
</code></pre>
<p>Build: <code>cc -o myapp myapp.c -L/usr/lib/system -lsystem_asl</code> (the rpath baked at install layout makes runtime resolution automatic). No <code>asl_open</code> needed — the implicit default client is created on first <code>asl_log</code>.</p>
<h4>Recommended: per-process client with facility</h4>
<pre><code>#include <asl.h>
static asl_object_t client;
int main(void)
{
/* facility = "com.example.myapp", options = 0 */
client = asl_open("myapp", "com.example.myapp", 0);
asl_log(client, NULL, ASL_LEVEL_NOTICE, "starting up");
/* ... */
asl_close(client);
return 0;
}
</code></pre>
<h4>Structured messages with key-value pairs</h4>
<pre><code>#include <asl.h>
asl_object_t msg = asl_new(ASL_TYPE_MSG);
asl_set(msg, ASL_KEY_MSG, "request completed");
asl_set(msg, ASL_KEY_LEVEL, ASL_STRING_NOTICE);
asl_set(msg, "Request-ID", request_id);
asl_set(msg, "Latency-ms", latency_str);
asl_send(client, msg);
asl_release(msg);
</code></pre>
<p>Querying / filtering / live-tail are covered in <code>asl.h</code> proper (<code>asl_search</code>, <code>asl_next</code>, <code>asl_match</code>); not needed for most daemons.</p>
<h4>BSD <code>syslog(3)</code> still works</h4>
<p>Existing code that calls <code>openlog()</code> / <code>syslog()</code> / <code>closelog()</code> needs <strong>zero changes</strong>. There are two paths and both reach ASL:</p>
<ol>
<li><strong>If linked against <code>libsystem_asl.so</code></strong>: the shim in <code>libsystem_asl.tproj/src/syslog.c</code> (288 lines) overrides libc's <code>syslog(3)</code> and routes directly to ASL via Mach IPC.</li>
<li><strong>If linked against FreeBSD libc only</strong>: <code>syslog(3)</code> writes RFC 5424 to <code>/var/run/log</code> AF_UNIX SOCK_DGRAM → Apple syslogd's new <code>bsd_in.c</code> module → ASL.</li>
</ol>
<p>Either path lands in <code>/var/log/asl/*.asl</code> (binary ASL store) and <code>/var/log/messages</code> (per <code>/etc/syslog.conf</code>).</p>
<h3>9.2 Objective-C</h3>
<p>ASL is a pure C API. Objective-C consumers call it identically — no Foundation wrapper required:</p>
<pre class="objc"><code>#import <Foundation/Foundation.h>
#import <asl.h>
@interface MyLogger : NSObject
@property (assign) asl_object_t client;
@end
@implementation MyLogger
- (instancetype)init {
if ((self = [super init])) {
_client = asl_open([@"MyApp" UTF8String],
[@"com.example.myapp" UTF8String], 0);
}
return self;
}
- (void)logRequest:(NSString *)requestID latency:(NSTimeInterval)ms {
asl_object_t msg = asl_new(ASL_TYPE_MSG);
asl_set(msg, ASL_KEY_MSG, "request completed");
asl_set(msg, ASL_KEY_LEVEL, ASL_STRING_NOTICE);
asl_set(msg, "Request-ID", [requestID UTF8String]);
asl_set(msg, "Latency-ms",
[[NSString stringWithFormat:@"%.0f", ms] UTF8String]);
asl_send(_client, msg);
asl_release(msg);
}
- (void)dealloc {
asl_close(_client);
}
@end
</code></pre>
<p>Build with the standard GNUstep / libobjc2 chain: <code>cc -fobjc-runtime=gnustep-2.0 myapp.m -lsystem_asl -lobjc -lgnustep-base -o myapp</code>. The libobjc2 runtime is unchanged by ASL — there is no ObjC bridge layer; logging is a flat C call from any method.</p>
<h4><code>NSLog()</code> behavior</h4>
<p>GNUstep's <code>NSLog()</code> writes to <code>stderr</code> by default. To have <code>NSLog()</code> output also land in ASL, install an <code>NSLog</code> handler that wraps <code>asl_log</code>:</p>
<pre class="objc"><code>static void asl_nslog_handler(NSString *fmt, va_list args) {
NSString *s = [[NSString alloc] initWithFormat:fmt arguments:args];
asl_log(NULL, NULL, ASL_LEVEL_NOTICE, "%s", [s UTF8String]);
}
/* In main, before any NSLog: */
_NSSetLogCStringFunction(asl_nslog_handler); /* GNUstep extension */
</code></pre>
<p>Alternative for new ObjC code: skip <code>NSLog()</code> entirely; call <code>asl_log()</code> directly — same as the C example above.</p>
<h3>9.3 What about <code>os_log()</code>?</h3>
<p>Apple's newer <code>os_log()</code> family (introduced on macOS 10.12 / iOS 10) is part of the closed-source <code>libsystem_trace</code> + kernel firehose. We're not porting that. Our <code>os/log.h</code> stub (Phase J1) provides macro definitions that expand to <code>asl_log()</code>, so any Apple source code that calls <code>os_log()</code> compiles and routes to ASL transparently — but new application code should call <code>asl_log()</code> directly for clarity. <code>os_log()</code> is preserved for source-compat with the Apple daemons we vendor; it's not the recommended consumer API for new C/ObjC code on this stack.</p>
<h3>9.4 Levels (priority constants)</h3>
<table>
<thead><tr><th>ASL constant</th><th>syslog(3) equiv</th><th>Numeric</th><th>Meaning</th></tr></thead>
<tbody>
<tr><td><code>ASL_LEVEL_EMERG</code></td><td><code>LOG_EMERG</code></td><td>0</td><td>System unusable</td></tr>
<tr><td><code>ASL_LEVEL_ALERT</code></td><td><code>LOG_ALERT</code></td><td>1</td><td>Action required immediately</td></tr>
<tr><td><code>ASL_LEVEL_CRIT</code></td><td><code>LOG_CRIT</code></td><td>2</td><td>Critical condition</td></tr>
<tr><td><code>ASL_LEVEL_ERR</code></td><td><code>LOG_ERR</code></td><td>3</td><td>Error</td></tr>
<tr><td><code>ASL_LEVEL_WARNING</code></td><td><code>LOG_WARNING</code></td><td>4</td><td>Warning</td></tr>
<tr><td><code>ASL_LEVEL_NOTICE</code></td><td><code>LOG_NOTICE</code></td><td>5</td><td>Normal but significant (default for daemons)</td></tr>
<tr><td><code>ASL_LEVEL_INFO</code></td><td><code>LOG_INFO</code></td><td>6</td><td>Informational</td></tr>
<tr><td><code>ASL_LEVEL_DEBUG</code></td><td><code>LOG_DEBUG</code></td><td>7</td><td>Debug-level message</td></tr>
</tbody>
</table>
<h3>9.5 Querying logs at runtime</h3>
<p>From the shell (after J5 ships <code>syslog(1)</code>):</p>
<pre class="shell"><code># All notice+ messages
syslog -k Level Nle 5
# Messages from a specific sender
syslog -k Sender myapp
# Live tail
syslog -w 0
# Standard BSD text log also works
tail -f /var/log/messages
</code></pre>
<h2 id="phases">10. Phased delivery</h2>
<div class="phase todo">
<h3>J0 — Repository setup <span class="pill info">~1-2 days</span></h3>
<p>Vendor <code>syslog/</code> and <code>Libnotify/</code> at <code>src/syslog/</code> and <code>src/Libnotify/</code> in the freebsd-launchd-mach repo. Initial commit of unmodified source as the diff baseline.</p>
</div>
<div class="phase todo">
<h3>J1 — libsystem_asl + libnotify (compile-only) <span class="pill info">~1-1.5 wk</span></h3>
<p>Build <code>libsystem_asl.so</code> + <code>libnotify.so</code> via bsd.lib.mk style Makefiles (mirroring libxpc/libdispatch). Wire MIG <code>asl_ipc.defs</code> and <code>notify_ipc.defs</code> via in-tree <code>migcom</code>. Install to <code>/usr/lib/system/</code>. Drop the <code>os/*</code> + <code>membership.h</code> + <code>configuration_profile.h</code> stubs. Suppress <code>__API_DEPRECATED</code> noise.</p>
<p><strong>Smoke marker</strong>: <code>ASL-LIB-OK</code> — hello binary calls <code>asl_log(NULL, NULL, 6, "hi")</code>, links + runs.</p>
</div>
<div class="phase todo">
<h3>J2 — syslogd + notifyd Mach RX <span class="pill info">~1.5-2 wk</span></h3>
<p>Build syslogd with <code>klog_in</code>/<code>udp_in</code>/<code>remote</code> modules disabled at <code>init_modules()</code> — Mach-only ingest via <code>database_server()</code> loop. Build notifyd similarly: minimal Mach-port server registering <code>com.apple.system.notification_center</code>. Both run under PID-1 launchd via plists.</p>
<p><strong>Smoke markers</strong>: <code>ASL-RUN-OK</code>, <code>NOTIFYD-RUN-OK</code>, <code>ASL-STORE-OK</code> (test <code>asl_log</code> → persisted to <code>/var/log/asl/</code>), <code>NOTIFY-ROUNDTRIP-OK</code>.</p>
</div>
<div class="phase todo">
<h3>J3 — bsd_in.c + /dev/klog + /etc/syslog.conf <span class="pill info">~1 wk</span></h3>
<p>Implement the three FreeBSD-interop patches: new <code>bsd_in.c</code> input module, RFC 5424 branch in <code>asl_syslog_input_convert()</code>, BSD-socket <code>Sockets</code> entry in plist. Enable <code>klog_in</code> + <code>bsd_out</code> modules. Ship minimal <code>/etc/syslog.conf</code>.</p>
<p><strong>Smoke markers</strong>: <code>SYSLOG-RESTORED-OK</code>, <code>KLOG-OK</code>.</p>
</div>
<div class="phase todo">
<h3>J4 — aslmanager + validation + smoke + hwregd handoff <span class="pill info">~2-3 wk</span></h3>
<p>Build aslmanager daemon (~2k LoC). Install at <code>/usr/libexec/aslmanager</code>. Wire launchd plist for periodic activation (Apple ships it as on-demand via XPC from syslogd; we can match). Without aslmanager <code>/var/log/asl/</code> grows unbounded — real disk-fillup consequence, not optional.</p>
<p>Full-boot CI assertion: PID-1 launchd boots; getty + hwregd + syslogd + notifyd + aslmanager all up; login: prompt appears; <code>tail /var/log/messages</code> shows getty/login session creation. Update <code>hwregd</code>'s <code>xlog()</code> to call <code>asl_log()</code> instead of <code>fprintf(stderr, ...)</code> — single ~5-line additive patch, no plist change needed.</p>
<p><strong>Smoke markers</strong>: <code>ASLMANAGER-OK</code>, <code>ASL-FROM-HWREGD-OK</code>, <code>BOOT-WITH-ASL-OK</code>.</p>
</div>
<div class="phase todo">
<h3>J5 — syslog(1) CLI <span class="pill info">~1-2 wk</span></h3>
<p>Final phase of the ASL port. ~2.9k LoC operator CLI. Required for structured queries against the binary <code>/var/log/asl/*.asl</code> database — without it operators have no shell-side ASL read access (only <code>tail /var/log/messages</code> via the BSD-text output route, which flattens structured fields). Also handles runtime daemon filter control (<code>syslog -c <pid> <mask></code>) and live-tail with structured filtering (<code>syslog -w 0 -k Sender hwregd</code>).</p>
<p>Send-side from shell is already covered by FreeBSD's existing <code>logger(1)</code> + our bsd_in.c socket compat, so syslog(1)'s send mode is redundant but harmless.</p>
<p><strong>Smoke marker</strong>: <code>SYSLOG-CLI-OK</code> — query roundtrip writes a message via libsystem_asl then reads it back via <code>syslog -k Sender hwregd</code>.</p>
</div>
<h2 id="smoke">11. CI smoke markers (full set)</h2>
<table>
<thead><tr><th>Marker</th><th>Phase</th><th>What it asserts</th></tr></thead>
<tbody>
<tr><td><code>ASL-BUILD-OK</code></td><td>J1</td><td>libsystem_asl.so + libnotify.so + syslogd + notifyd binaries built + installed</td></tr>
<tr><td><code>ASL-LIB-OK</code></td><td>J1</td><td>Hello binary links + runs against libsystem_asl + libnotify</td></tr>
<tr><td><code>ASL-RUN-OK</code></td><td>J2</td><td>syslogd starts under PID-1 launchd, registers Mach service</td></tr>
<tr><td><code>NOTIFYD-RUN-OK</code></td><td>J2</td><td>notifyd starts under PID-1 launchd, registers Mach service</td></tr>
<tr><td><code>ASL-STORE-OK</code></td><td>J2</td><td>Test client <code>asl_log()</code> → persists to <code>/var/log/asl/</code></td></tr>
<tr><td><code>NOTIFY-ROUNDTRIP-OK</code></td><td>J2</td><td>Client A posts; client B (registered watcher) receives via Mach</td></tr>
<tr><td><code>SYSLOG-RESTORED-OK</code></td><td>J3</td><td>FreeBSD-base <code>syslog(3)</code> caller lands in ASL + <code>/var/log/messages</code></td></tr>
<tr><td><code>KLOG-OK</code></td><td>J3</td><td>Kernel <code>printf</code> appears in ASL</td></tr>
<tr><td><code>ASL-FROM-HWREGD-OK</code></td><td>J4</td><td>hwregd's <code>asl_log()</code> calls appear in <code>/var/log/asl/</code></td></tr>
<tr><td><code>BOOT-WITH-ASL-OK</code></td><td>J4</td><td>Full PID-1 boot to login with all daemons running</td></tr>
<tr><td><code>SYSLOG-CLI-OK</code></td><td>J5</td><td>syslog(1) query roundtrip: write via libsystem_asl, read back via <code>syslog -k Sender hwregd</code></td></tr>
</tbody>
</table>
<h2 id="launchd-log">12. launchd <code>log.c</code> interaction</h2>
<p>launchd-842 has its own log subsystem in <code>src/launchd/src/log.c</code> using <code>launchd_syslog()</code>. On macOS the internal backend writes to ASL via <code>asl_log_message()</code>; in our port the backend currently writes to <code>/dev/console</code>.</p>
<p><strong>Decision</strong>: keep the BSD path for now. Once ASL is up, optionally swap launchd's internal backend to call <code>asl_log_message()</code> — behind-the-scenes change in log.c, no callsite churn across launchd's 28k LoC. The BSD-only backend stays useful as a fallback during early boot before syslogd is reachable. Additive, not undoing.</p>
<h2 id="open-questions">13. Open questions</h2>
<ol>
<li><strong>asl.conf merge policy (RESOLVED)</strong>: <strong>ship a custom ~10-line FreeBSD-specific config</strong>. Neither Apple variant is right — osx variant has dead rules for services we don't have (loginwindow, WindowServer, CrashReporter), ios variant lacks the system.log BSD-text writer. Custom is simpler than either and covers exactly what we need: ASL DB destination + system.log BSD-text writer + level-notice store rule + kernel-sender rule.</li>
<li><strong>notifyd PID-1 ordering (RESOLVED)</strong>: <strong>no explicit ordering needed — rely on launchd's MachServices port-on-behalf pattern</strong>, same as Apple does on macOS. launchd holds the receive-right for any declared MachServices name as soon as the plist loads at boot, so <code>bootstrap_look_up()</code> returns a usable send-right immediately, even before the target daemon runs. First message queues in the kernel Mach port, triggers on-demand activation, drains when the daemon's <code>mach_msg_server</code> starts. Our launchd-842 port already implements this — it's how <code>com.apple.bootstrap_server</code> works (proved in <code>phase_g2_complete</code>).</li>
<li><strong>fileport syscall surface (RESOLVED)</strong>: notifyd + ASL need <code>sys/fileport.h</code>'s <code>fileport_makefd</code> / <code>fileport_makeport</code>. Apple implements as kernel Mach traps (XNU), with the kernel tracking fileglob ↔ Mach-port associations and supporting atomic fd-passing inside Mach message bodies. <strong>Decision</strong>: same architecture on FreeBSD via new ops in our mach.ko trap multiplexer (slot 219). Implementation: <code>fget(td, fd, ...)</code> + <code>fhold()</code> for makeport, <code>falloc_noinstall()</code> + <code>finstall()</code> for makefd, store <code>struct file *</code> in the Mach port's kobject slot. ~200-300 LoC, same shape as the <code>task_get/set_special_port</code> ops we added in Phase G prereqs. Slotted as a J2 prereq; bumps J2 from ~1.5-2 wk to ~2.5-3 wk.</li>
<li><strong>Log rotation (RESOLVED)</strong>: aslmanager ships alongside ASL in J4, not deferred. Without it <code>/var/log/asl/</code> grows unbounded — real disk-fillup consequence, not optional for a shipping system.</li>
<li><strong>hwregd asl_log() swap timing</strong>: part of J4 (clean handoff in the ASL plan) or separate hwregd iter-2 commit? <em>Leaning toward J4 for atomicity — the ASL plan ships with at least one real consumer wired up.</em></li>
</ol>
<h2 id="sources">14. Source citations</h2>
<h3>Apple source (vendored locally)</h3>
<ul>
<li><code>/Users/jmaloney/Documents/launchd/syslog/syslog.xcodeproj/project.pbxproj</code> — Xcode targets, MIG defs</li>
<li><code>/Users/jmaloney/Documents/launchd/syslog/aslcommon/asl_ipc.defs</code> — MIG subsystem 114</li>
<li><code>/Users/jmaloney/Documents/launchd/syslog/libsystem_asl.tproj/include/asl.h</code> — public API</li>
<li><code>/Users/jmaloney/Documents/launchd/syslog/libsystem_asl.tproj/src/asl.c</code> — client core</li>
<li><code>/Users/jmaloney/Documents/launchd/syslog/libsystem_asl.tproj/src/syslog.c</code> — BSD <code>syslog(3)</code> shim</li>
<li><code>/Users/jmaloney/Documents/launchd/syslog/syslogd.tproj/syslogd.c</code> — daemon main</li>
<li><code>/Users/jmaloney/Documents/launchd/syslog/syslogd.tproj/dbserver.c</code> — Mach RX loop</li>
<li><code>/Users/jmaloney/Documents/launchd/syslog/syslogd.tproj/daemon.c:1057,1121</code> — parser to patch</li>
<li><code>/Users/jmaloney/Documents/launchd/syslog/syslogd.tproj/klog_in.c</code> — /dev/klog reader</li>
<li><code>/Users/jmaloney/Documents/launchd/syslog/syslogd.tproj/bsd_out.c</code> — /etc/syslog.conf parser</li>
<li><code>/Users/jmaloney/Documents/launchd/syslog/syslogd.tproj/asl_action.c</code> — output module</li>
<li><code>/Users/jmaloney/Documents/launchd/syslog/syslogd.tproj/com.apple.syslogd.plist</code> — launchd plist</li>
<li><code>/Users/jmaloney/Documents/launchd/syslog/aslcommon/asl_common.c</code> — config + module merge</li>
<li><code>/Users/jmaloney/Documents/launchd/Libnotify/notifyd/notifyd.c</code> — notifyd main + deps</li>
<li><code>/Users/jmaloney/Documents/launchd/Libnotify/notify_client.c</code> + <code>libnotify.c</code> — client API</li>
<li><code>/Users/jmaloney/Documents/launchd/IOKitUser/IOKitPMUnitTests/Stubs/NotifyStubs.{h,m}</code> — Apple's own notify-stub template (reference shape)</li>
</ul>
<h3>FreeBSD source</h3>
<ul>
<li><code>freebsd-src/lib/libc/gen/syslog.c</code> — libc <code>syslog(3)</code> emits RFC 5424</li>
<li><code>freebsd-src/sys/kern/subr_log.c</code> — <code>/dev/klog</code> cdev</li>
<li><code>freebsd-src/sys/sys/syslog.h</code> — <code>_PATH_LOG</code></li>
<li><code>freebsd-src/contrib/openbsm/bsm/libbsm.h:1257-1294</code> — identical audit_token signatures</li>
</ul>
<h3>Related project memory</h3>
<ul>
<li><code>phase_e_libdispatch_mach_recv</code> — Mach RECV backend; syslogd's <code>database_server()</code> uses the same pattern</li>
<li><code>phase_g2_complete</code> — bootstrap server; syslogd + notifyd register via this</li>
<li><code>phase_h_libxpc_complete</code> — libxpc already built; ASL uses XPC entitlement queries</li>
<li><code>phase_i1c_launchd_built</code> — launchd-842 built; <code>LAUNCH_JOBKEY_MACHSERVICES</code> hands Mach port</li>
<li><code>phase_i3_pid1_launchd_boot</code> — PID-1 launchd boots to login; syslogd + notifyd run as launchd daemons</li>
<li><code>install_layout_policies</code> — <code>/usr/lib/system/</code> install path</li>
<li><code>freebsd_launchd_mach_pkgbase_migration</code> — FreeBSD-syslogd removed at 88694f0</li>
</ul>
<hr>
<p class="footnote">Plan synthesized from five parallel research agents on 2026-05-16:
(1) Apple syslog source audit, (2) libnotify dependency analysis, (3) FreeBSD syslog interop audit, (4) existing plan validation, (5) ASL + IOKit shared-dep audit. <strong>Supersedes the earlier <code>freebsd-asl-plan.html</code> v0</strong> (which assumed an AF_UNIX / GNUstep Distributed Objects substrate — wrong now that Mach IPC + libxpc + libdispatch + libCoreFoundation are all working). Section 9 (Consumer guide) added per user request: documents how C and Objective-C consumers use ASL after this plan ships. Please flag anything that should change before this becomes a committed plan.</p>
<p class="footnote"><strong>Revision 2026-05-23:</strong> filed under the <code>freebsd-launchd-mach</code> (v2) effort; ASL stack has SHIPPED (Phase J1 + J2 in <code>build.sh</code>, CI marker <code>SYSLOG-RUN-OK</code> green). No technical content changed — framing only.</p>
</body>
</html>