Skip to content

Commit f784d41

Browse files
committed
[cron] fixes
1 parent aff2b1b commit f784d41

File tree

5 files changed

+40
-20
lines changed

5 files changed

+40
-20
lines changed

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ members = [
3434
"uucp",
3535
"xform",
3636
"i18n",
37-
"cron",
3837
]
3938

4039
[workspace.package]

cron/at.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1347,7 +1347,7 @@ mod timespec {
13471347
TimeDateIncrement {
13481348
time: Time,
13491349
date: Date,
1350-
inrement: Increment,
1350+
increment: Increment,
13511351
},
13521352
Nowspec(Nowspec),
13531353
}
@@ -1397,12 +1397,12 @@ mod timespec {
13971397
let date = Date::from_str(&s[time_index..date_index])?;
13981398

13991399
if date_index != string_length {
1400-
let inrement = Increment::from_str(&s[date_index..])?;
1400+
let increment = Increment::from_str(&s[date_index..])?;
14011401

14021402
return Ok(Self::TimeDateIncrement {
14031403
time,
14041404
date,
1405-
inrement,
1405+
increment,
14061406
});
14071407
}
14081408

@@ -1439,15 +1439,15 @@ mod timespec {
14391439
Timespec::TimeDateIncrement {
14401440
time,
14411441
date,
1442-
inrement,
1442+
increment,
14431443
} => {
14441444
let time = time.to_naive_time()?;
14451445
let date = date.to_naive_date()?;
14461446

14471447
let date_time = NaiveDateTime::new(date, time)
14481448
.and_utc()
14491449
.checked_add_signed(
1450-
chrono::TimeDelta::from_std(inrement.to_duration()).ok()?,
1450+
chrono::TimeDelta::from_std(increment.to_duration()).ok()?,
14511451
)?;
14521452

14531453
match date_time < Utc::now() {
@@ -1889,7 +1889,7 @@ mod timespec {
18891889
day_number: DayNumber(NonZero::new(4).expect("valid")),
18901890
year_number: YearNumber(2024)
18911891
},
1892-
inrement: Increment::Plus {
1892+
increment: Increment::Plus {
18931893
number: 1,
18941894
period: IncPeriod::Day
18951895
}
@@ -1933,7 +1933,7 @@ mod timespec {
19331933
day_number: DayNumber(NonZero::new(4).expect("valid")),
19341934
year_number: YearNumber(3000),
19351935
},
1936-
inrement: Increment::Plus {
1936+
increment: Increment::Plus {
19371937
number: 1,
19381938
period: IncPeriod::Day,
19391939
},

cron/crond.rs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@ fn acquire_lock() -> Result<File, CronError> {
109109
.open(PID_FILE)
110110
.map_err(CronError::PidFile)?;
111111

112+
// SAFETY: flock() is safe to call here because:
113+
// 1. file.as_raw_fd() returns a valid file descriptor from an open File
114+
// 2. LOCK_EX | LOCK_NB are valid flags for exclusive non-blocking lock
115+
// 3. We check the return value and handle errors appropriately
112116
unsafe {
113117
if libc::flock(file.as_raw_fd(), libc::LOCK_EX | libc::LOCK_NB) != 0 {
114118
return Err(CronError::AlreadyRunning);
@@ -124,6 +128,12 @@ fn acquire_lock() -> Result<File, CronError> {
124128

125129
/// Create new daemon process of crond
126130
fn setup() -> i32 {
131+
// SAFETY: These libc calls implement the standard Unix daemon pattern:
132+
// 1. fork() creates child process; parent exits, child continues
133+
// 2. setsid() creates new session, detaches from controlling terminal
134+
// 3. chdir("/") prevents holding directory mounts open
135+
// 4. close(STD*_FILENO) closes inherited file descriptors
136+
// All calls have defined behavior and we check fork() return value.
127137
unsafe {
128138
use libc::*;
129139

@@ -153,10 +163,11 @@ extern "C" fn handle_sighup(_: libc::c_int) {
153163

154164
/// Handles SIGCHLD signal to reap zombie child processes
155165
extern "C" fn handle_sigchld(_: libc::c_int) {
156-
unsafe {
157-
// Reap all zombie children without blocking
158-
while libc::waitpid(-1, std::ptr::null_mut(), libc::WNOHANG) > 0 {}
159-
}
166+
// SAFETY: waitpid() is async-signal-safe per POSIX.
167+
// -1 means wait for any child, WNOHANG returns immediately if no zombie.
168+
// null status pointer is valid when we don't need exit status.
169+
// Loop reaps all available zombies without blocking.
170+
unsafe { while libc::waitpid(-1, std::ptr::null_mut(), libc::WNOHANG) > 0 {} }
160171
}
161172

162173
/// Daemon loop
@@ -203,6 +214,9 @@ fn main() -> Result<(), Box<dyn Error>> {
203214
// Acquire PID file lock (keep handle alive for the daemon's lifetime)
204215
let _pid_lock = acquire_lock()?;
205216

217+
// SAFETY: signal() is safe to call with valid signal numbers and
218+
// extern "C" function handlers. SIGHUP and SIGCHLD are standard signals.
219+
// The handlers are async-signal-safe (only call async-signal-safe functions).
206220
unsafe {
207221
libc::signal(libc::SIGHUP, handle_sighup as usize);
208222
libc::signal(libc::SIGCHLD, handle_sigchld as usize);
@@ -212,5 +226,7 @@ fn main() -> Result<(), Box<dyn Error>> {
212226
}
213227

214228
fn sleep(target: u32) {
229+
// SAFETY: libc::sleep() is safe with any u32 value; it simply
230+
// suspends execution for the specified number of seconds.
215231
unsafe { libc::sleep(target) };
216232
}

cron/job.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,11 @@ impl CronJob {
294294
}
295295

296296
pub fn run_job(&self) -> std::io::Result<()> {
297+
// SAFETY: fork() is safe to call here because:
298+
// 1. We immediately check the return value for errors (pid < 0)
299+
// 2. The child process (pid == 0) immediately exec()s a new process
300+
// 3. The parent process returns immediately without shared state issues
301+
// 4. We use Command::exec() which replaces the child process entirely
297302
unsafe {
298303
let pid = libc::fork();
299304
if pid < 0 {

cron/tests/at/mod.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ fn test4() {
136136
}
137137

138138
#[test]
139-
fn test6() {
139+
fn test5() {
140140
let _lock = TEST_MUTEX.lock().unwrap();
141141
let (_temp_dir, dir_path) = setup_test_env();
142142
fs::create_dir(&dir_path).expect("Unable to create test directory");
@@ -159,7 +159,7 @@ fn test6() {
159159
}
160160

161161
#[test]
162-
fn test7() {
162+
fn test6() {
163163
let _lock = TEST_MUTEX.lock().unwrap();
164164
let (_temp_dir, dir_path) = setup_test_env();
165165
fs::create_dir(&dir_path).expect("Unable to create test directory");
@@ -182,7 +182,7 @@ fn test7() {
182182
}
183183

184184
#[test]
185-
fn test8() {
185+
fn test7() {
186186
let _lock = TEST_MUTEX.lock().unwrap();
187187
let (_temp_dir, dir_path) = setup_test_env();
188188
fs::create_dir(&dir_path).expect("Unable to create test directory");
@@ -205,7 +205,7 @@ fn test8() {
205205
}
206206

207207
#[test]
208-
fn test9() {
208+
fn test8() {
209209
let _lock = TEST_MUTEX.lock().unwrap();
210210
let (_temp_dir, dir_path) = setup_test_env();
211211
fs::create_dir(&dir_path).expect("Unable to create test directory");
@@ -228,7 +228,7 @@ fn test9() {
228228
}
229229

230230
#[test]
231-
fn test10() {
231+
fn test9() {
232232
let _lock = TEST_MUTEX.lock().unwrap();
233233
let (_temp_dir, dir_path) = setup_test_env();
234234
fs::create_dir(&dir_path).expect("Unable to create test directory");
@@ -250,7 +250,7 @@ fn test10() {
250250
}
251251

252252
#[test]
253-
fn test11() {
253+
fn test10() {
254254
let _lock = TEST_MUTEX.lock().unwrap();
255255
let (_temp_dir, dir_path) = setup_test_env();
256256
fs::create_dir(&dir_path).expect("Unable to create test directory");
@@ -279,7 +279,7 @@ fn test11() {
279279
}
280280

281281
#[test]
282-
fn test12() {
282+
fn test11() {
283283
let _lock = TEST_MUTEX.lock().unwrap();
284284
let (_temp_dir, dir_path) = setup_test_env();
285285
fs::create_dir(&dir_path).expect("Unable to create test directory");
@@ -309,7 +309,7 @@ fn test12() {
309309
}
310310

311311
#[test]
312-
fn test13() {
312+
fn test12() {
313313
let _lock = TEST_MUTEX.lock().unwrap();
314314
let (_temp_dir, dir_path) = setup_test_env();
315315
fs::create_dir(&dir_path).expect("Unable to create test directory");

0 commit comments

Comments
 (0)