Skip to content

Commit f62f2d4

Browse files
committed
Add daemon() builtin function to KernelScript.
Signed-off-by: Cong Wang <cwang@multikernel.io>
1 parent a6ebbe9 commit f62f2d4

File tree

4 files changed

+112
-3
lines changed

4 files changed

+112
-3
lines changed

SPEC.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4188,6 +4188,21 @@ mod program {
41884188
// - For Cgroup: target is cgroup path (e.g., "/sys/fs/cgroup/test"), flags are unused (0)
41894189
pub fn attach(handle: ProgramHandle, target: string, flags: u32) -> u32
41904190
4191+
// Detach a loaded eBPF program from its current attachment
4192+
// - Automatically determines the correct detachment method based on program type
4193+
// - Safe to call multiple times on the same handle (no-op if already detached)
4194+
// - No return value (void) - logs errors to stderr if detachment fails
4195+
pub fn detach(handle: ProgramHandle) -> void
4196+
4197+
// Convert the current userspace process into a daemon and run indefinitely
4198+
// - Daemonizes the process using standard Unix daemon() call
4199+
// - Sets up proper signal handling (SIGTERM, SIGINT for graceful shutdown)
4200+
// - Creates PID file for process management
4201+
// - This function NEVER returns - it runs an infinite loop
4202+
// - Only available in userspace context - compilation error if used in eBPF
4203+
// - Process exits gracefully on receiving termination signals
4204+
pub fn daemon() -> never
4205+
41914206
// Register struct_ops impl blocks
41924207
pub fn register(ops) -> i32
41934208
}
@@ -4647,6 +4662,38 @@ fn print_summary_stats() {
46474662
}
46484663
```
46494664

4665+
### 14.4 Daemon Service Features
4666+
4667+
The `daemon()` builtin provides several key features for long-running services:
4668+
4669+
1. **Process Daemonization**: Automatically detaches from terminal and runs in background
4670+
2. **Signal Handling**: Graceful shutdown on SIGTERM/SIGINT signals
4671+
3. **Process Management**: Creates PID file for system service integration
4672+
4. **Infinite Runtime**: Never returns - runs monitoring loop forever
4673+
5. **Error Handling**: Proper cleanup on signals and exit conditions
4674+
4675+
#### Usage with System Services
4676+
4677+
```bash
4678+
# Compile daemon service
4679+
kernelscript compile network_monitor.ks
4680+
4681+
# Run as system service (systemd example)
4682+
# /etc/systemd/system/network-monitor.service:
4683+
[Unit]
4684+
Description=KernelScript Network Monitor
4685+
After=network.target
4686+
4687+
[Service]
4688+
Type=forking
4689+
ExecStart=/usr/local/bin/network_monitor
4690+
PIDFile=/var/run/network_monitor.pid
4691+
Restart=always
4692+
4693+
[Install]
4694+
WantedBy=multi-user.target
4695+
```
4696+
46504697
## 15. Complete Formal Grammar (EBNF)
46514698

46524699
```ebnf

examples/userspace_example.ks

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@ fn main() -> i32 {
4646
// Show userspace functionality working
4747
detach(prog)
4848
print("Userspace example program detached")
49-
49+
50+
print("Now running as a daemon")
51+
daemon() // Never returns
5052
return 0
5153
}
5254

src/stdlib.ml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,17 @@ let builtin_functions = [
177177
kernel_impl = "";
178178
validate = Some validate_dispatch_function;
179179
};
180+
{
181+
name = "daemon";
182+
param_types = []; (* No parameters - void function *)
183+
return_type = Void; (* Never returns in practice, but type system needs Void *)
184+
description = "Become a daemon process - detaches from terminal and runs forever (userspace only)";
185+
is_variadic = false;
186+
ebpf_impl = ""; (* Not available in eBPF context *)
187+
userspace_impl = "daemon_builtin"; (* Custom implementation in userspace *)
188+
kernel_impl = ""; (* Not available in kernel context *)
189+
validate = None;
190+
};
180191

181192
]
182193

src/userspace_codegen.ml

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,7 @@ type function_usage = {
445445
mutable uses_attach: bool;
446446
mutable uses_detach: bool;
447447
mutable uses_map_operations: bool;
448+
mutable uses_daemon: bool;
448449
mutable used_maps: string list;
449450
mutable used_dispatch_functions: int list;
450451
}
@@ -454,6 +455,7 @@ let create_function_usage () = {
454455
uses_attach = false;
455456
uses_detach = false;
456457
uses_map_operations = false;
458+
uses_daemon = false;
457459
used_maps = [];
458460
used_dispatch_functions = [];
459461
}
@@ -725,6 +727,7 @@ let track_function_usage ctx instr =
725727
| "load" -> ctx.function_usage.uses_load <- true
726728
| "attach" -> ctx.function_usage.uses_attach <- true
727729
| "detach" -> ctx.function_usage.uses_detach <- true
730+
| "daemon" -> ctx.function_usage.uses_daemon <- true
728731
| "dispatch" ->
729732
let num_buffers = List.length args in
730733
if not (List.mem num_buffers ctx.function_usage.used_dispatch_functions) then
@@ -2915,6 +2918,7 @@ let generate_complete_userspace_program_from_ir ?(config_declarations = []) ?(ty
29152918
uses_attach = acc_usage.uses_attach || func_usage.uses_attach;
29162919
uses_detach = acc_usage.uses_detach || func_usage.uses_detach;
29172920
uses_map_operations = acc_usage.uses_map_operations || func_usage.uses_map_operations;
2921+
uses_daemon = acc_usage.uses_daemon || func_usage.uses_daemon;
29182922
used_maps = List.fold_left (fun acc map_name ->
29192923
if List.mem map_name acc then acc else map_name :: acc
29202924
) acc_usage.used_maps func_usage.used_maps;
@@ -3537,12 +3541,56 @@ static int add_attachment(int prog_fd, const char *target, uint32_t flags,
35373541
35383542
let bpf_obj_decl = "" in (* Skeleton now handles the BPF object *)
35393543
3540-
let functions_list = List.filter (fun s -> s <> "") [attachment_storage; load_function; attach_function; detach_function] in
3544+
(* Generate daemon function if used *)
3545+
let daemon_function = if all_usage.uses_daemon then
3546+
sprintf {|void daemon_builtin(void) {
3547+
// Standard Unix daemon process
3548+
if (daemon(0, 0) != 0) {
3549+
perror("daemon");
3550+
exit(1);
3551+
}
3552+
3553+
// Setup daemon infrastructure
3554+
signal(SIGTERM, handle_signal);
3555+
signal(SIGINT, handle_signal);
3556+
signal(SIGHUP, SIG_IGN);
3557+
3558+
// Create PID file
3559+
FILE *pidfile = fopen("/var/run/%s.pid", "w");
3560+
if (pidfile) {
3561+
fprintf(pidfile, "%%d\n", getpid());
3562+
fclose(pidfile);
3563+
}
3564+
3565+
// Daemon main loop - never returns
3566+
while (keep_running) {
3567+
sleep(1);
3568+
}
3569+
3570+
// Cleanup and exit
3571+
unlink("/var/run/%s.pid");
3572+
exit(0);
3573+
}|} base_name base_name
3574+
else "" in
3575+
3576+
let functions_list = List.filter (fun s -> s <> "") [attachment_storage; load_function; attach_function; detach_function; daemon_function] in
35413577
if functions_list = [] && bpf_obj_decl = "" then ""
35423578
else
35433579
sprintf "\n/* BPF Helper Functions (generated only when used) */\n%s\n\n%s"
35443580
bpf_obj_decl (String.concat "\n\n" functions_list) in
35453581
3582+
(* Generate daemon signal handling variables if used *)
3583+
let daemon_globals = if all_usage.uses_daemon then
3584+
sprintf {|
3585+
// Daemon signal handling
3586+
static volatile sig_atomic_t keep_running = 1;
3587+
3588+
static void handle_signal(int sig) {
3589+
keep_running = 0;
3590+
}
3591+
|}
3592+
else "" in
3593+
35463594
(* Generate struct_ops attach functions *)
35473595
let struct_ops_attach_functions = generate_struct_ops_attach_functions ir_multi_prog in
35483596
@@ -3552,6 +3600,7 @@ static int add_attachment(int prog_fd, const char *target, uint32_t flags,
35523600
35533601
%s
35543602
3603+
%s
35553604
%s
35563605
35573606
%s
@@ -3575,7 +3624,7 @@ static int add_attachment(int prog_fd, const char *target, uint32_t flags,
35753624
%s
35763625
35773626
%s
3578-
|} includes string_typedefs type_alias_definitions string_helpers enum_definitions structs_with_pinned skeleton_code all_fd_declarations map_operation_functions ringbuf_handlers ringbuf_dispatch_functions auto_bpf_init_code getopt_parsing_code bpf_helper_functions struct_ops_attach_functions functions
3627+
|} includes string_typedefs type_alias_definitions string_helpers daemon_globals enum_definitions structs_with_pinned skeleton_code all_fd_declarations map_operation_functions ringbuf_handlers ringbuf_dispatch_functions auto_bpf_init_code getopt_parsing_code bpf_helper_functions struct_ops_attach_functions functions
35793628
35803629
(** Generate userspace C code from IR multi-program *)
35813630
let generate_userspace_code_from_ir ?(config_declarations = []) ?(type_aliases = []) ?(tail_call_analysis = {Tail_call_analyzer.dependencies = []; prog_array_size = 0; index_mapping = Hashtbl.create 16; errors = []}) ?(kfunc_dependencies = {kfunc_definitions = []; private_functions = []; program_dependencies = []; module_name = ""}) ?(resolved_imports = []) ?symbol_table ?btf_path (ir_multi_prog : ir_multi_program) ?(output_dir = ".") source_filename =

0 commit comments

Comments
 (0)