1#![doc = include_str!("../README.md")]
21#![cfg(unix)]
22
23mod error;
24
25#[cfg(feature = "tester")]
26pub mod tester_lib;
27
28extern crate errno;
29extern crate libc;
30
31use std::env::set_current_dir;
32use std::ffi::{CStr, CString};
33use std::fmt;
34use std::fs::File;
35use std::os::fd::OwnedFd;
36use std::os::unix::ffi::OsStringExt;
37use std::os::unix::io::AsRawFd;
38use std::os::unix::process::ExitStatusExt;
39use std::path::PathBuf;
40use std::process::{ExitStatus, exit};
41
42use self::error::check_err;
43
44pub use self::error::{Error, ErrorKind};
45
46#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
47#[cfg_attr(
48 feature = "tester",
49 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
50)]
51enum UserImpl {
52 Name(String),
53 Id(libc::uid_t),
54}
55
56#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
58#[cfg_attr(
59 feature = "tester",
60 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
61)]
62pub struct User {
63 inner: UserImpl,
64}
65
66impl From<&str> for User {
67 fn from(t: &str) -> User {
68 User {
69 inner: UserImpl::Name(t.to_owned()),
70 }
71 }
72}
73
74impl From<u32> for User {
75 fn from(t: u32) -> User {
76 User {
77 inner: UserImpl::Id(t as libc::uid_t),
78 }
79 }
80}
81
82#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
83#[cfg_attr(
84 feature = "tester",
85 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
86)]
87enum GroupImpl {
88 Name(String),
89 Id(libc::gid_t),
90}
91
92#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
94#[cfg_attr(
95 feature = "tester",
96 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
97)]
98pub struct Group {
99 inner: GroupImpl,
100}
101
102impl From<&str> for Group {
103 fn from(t: &str) -> Group {
104 Group {
105 inner: GroupImpl::Name(t.to_owned()),
106 }
107 }
108}
109
110impl From<u32> for Group {
111 fn from(t: u32) -> Group {
112 Group {
113 inner: GroupImpl::Id(t as libc::gid_t),
114 }
115 }
116}
117
118#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
120#[cfg_attr(
121 feature = "tester",
122 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
123)]
124pub struct Mask {
125 inner: libc::mode_t,
126}
127
128impl From<u32> for Mask {
129 fn from(inner: u32) -> Mask {
130 Mask {
131 inner: inner as libc::mode_t,
132 }
133 }
134}
135
136#[derive(Debug)]
137enum StdioImpl {
138 Devnull,
139 RedirectToFile(File),
140 RedirectToFd(OwnedFd),
141 Keep,
142}
143
144#[derive(Debug)]
146pub struct Stdio {
147 inner: StdioImpl,
148}
149
150impl Stdio {
151 pub fn devnull() -> Self {
152 Self {
153 inner: StdioImpl::Devnull,
154 }
155 }
156
157 pub fn keep() -> Self {
158 Self {
159 inner: StdioImpl::Keep,
160 }
161 }
162}
163
164impl From<File> for Stdio {
165 fn from(file: File) -> Self {
166 Self {
167 inner: StdioImpl::RedirectToFile(file),
168 }
169 }
170}
171
172impl From<OwnedFd> for Stdio {
173 fn from(fd: OwnedFd) -> Self {
174 Self {
175 inner: StdioImpl::RedirectToFd(fd),
176 }
177 }
178}
179
180#[derive(Debug, PartialEq, Eq)]
182#[non_exhaustive]
183pub struct Parent {
184 pub first_child_exit_status: ExitStatus,
185}
186
187#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
189#[non_exhaustive]
190pub struct Child<T> {
191 pub privileged_action_result: T,
192}
193
194#[derive(Debug, PartialEq, Eq)]
197pub enum Outcome<T> {
198 Parent(Result<Parent, Error>),
199 Child(Result<Child<T>, Error>),
200}
201
202impl<T> Outcome<T> {
203 pub fn is_parent(&self) -> bool {
204 match self {
205 Outcome::Parent(_) => true,
206 Outcome::Child(_) => false,
207 }
208 }
209
210 pub fn is_child(&self) -> bool {
211 match self {
212 Outcome::Parent(_) => false,
213 Outcome::Child(_) => true,
214 }
215 }
216}
217
218pub struct Daemonize<T> {
234 directory: Option<PathBuf>,
235 pid_file: Option<PathBuf>,
236 chown_pid_file_user: Option<User>,
237 chown_pid_file_group: Option<Group>,
238 user: Option<User>,
239 group: Option<Group>,
240 umask: Mask,
241 root: Option<PathBuf>,
242 privileged_action: Box<dyn FnOnce() -> T>,
243 stdin: Stdio,
244 stdout: Stdio,
245 stderr: Stdio,
246}
247
248impl<T> fmt::Debug for Daemonize<T> {
249 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
250 fmt.debug_struct("Daemonize")
251 .field("directory", &self.directory)
252 .field("pid_file", &self.pid_file)
253 .field("chown_pid_file_user", &self.chown_pid_file_user)
254 .field("chown_pid_file_group", &self.chown_pid_file_group)
255 .field("user", &self.user)
256 .field("group", &self.group)
257 .field("umask", &self.umask)
258 .field("root", &self.root)
259 .field("stdin", &self.stdin)
260 .field("stdout", &self.stdout)
261 .field("stderr", &self.stderr)
262 .finish()
263 }
264}
265
266impl Default for Daemonize<()> {
267 fn default() -> Self {
268 Self::new()
269 }
270}
271
272impl Daemonize<()> {
273 pub fn new() -> Self {
274 Daemonize {
275 directory: None,
276 pid_file: None,
277 chown_pid_file_user: None,
278 chown_pid_file_group: None,
279 user: None,
280 group: None,
281 umask: 0o027.into(),
282 privileged_action: Box::new(|| ()),
283 root: None,
284 stdin: Stdio::devnull(),
285 stdout: Stdio::devnull(),
286 stderr: Stdio::devnull(),
287 }
288 }
289}
290
291impl<T> Daemonize<T> {
292 pub fn pid_file<F: Into<PathBuf>>(mut self, path: F) -> Self {
294 self.pid_file = Some(path.into());
295 self
296 }
297
298 pub fn chown_pid_file_user<U: Into<User>>(mut self, user: U) -> Self {
300 self.chown_pid_file_user = Some(user.into());
301 self
302 }
303
304 pub fn chown_pid_file_group<G: Into<Group>>(mut self, group: G) -> Self {
306 self.chown_pid_file_group = Some(group.into());
307 self
308 }
309
310 pub fn working_directory<F: Into<PathBuf>>(mut self, path: F) -> Self {
312 self.directory = Some(path.into());
313 self
314 }
315
316 pub fn user<U: Into<User>>(mut self, user: U) -> Self {
318 self.user = Some(user.into());
319 self
320 }
321
322 pub fn group<G: Into<Group>>(mut self, group: G) -> Self {
324 self.group = Some(group.into());
325 self
326 }
327
328 pub fn umask<M: Into<Mask>>(mut self, mask: M) -> Self {
330 self.umask = mask.into();
331 self
332 }
333
334 pub fn chroot<F: Into<PathBuf>>(mut self, path: F) -> Self {
336 self.root = Some(path.into());
337 self
338 }
339
340 pub fn privileged_action<N, F: FnOnce() -> N + 'static>(self, action: F) -> Daemonize<N> {
343 Daemonize {
344 directory: self.directory,
345 pid_file: self.pid_file,
346 chown_pid_file_user: self.chown_pid_file_user,
347 chown_pid_file_group: self.chown_pid_file_group,
348 user: self.user,
349 group: self.group,
350 umask: self.umask,
351 root: self.root,
352 privileged_action: Box::new(action),
353 stdin: self.stdin,
354 stdout: self.stdout,
355 stderr: self.stderr,
356 }
357 }
358
359 pub fn stdout<S: Into<Stdio>>(mut self, stdio: S) -> Self {
361 self.stdout = stdio.into();
362 self
363 }
364
365 pub fn stderr<S: Into<Stdio>>(mut self, stdio: S) -> Self {
367 self.stderr = stdio.into();
368 self
369 }
370
371 pub unsafe fn start(self) -> Result<T, Error> {
379 unsafe {
380 match self.execute() {
381 Outcome::Parent(Ok(Parent {
382 first_child_exit_status,
383 })) => exit(
384 first_child_exit_status
385 .code()
386 .unwrap_or_else(|| libc::abort()),
387 ),
388 Outcome::Parent(Err(err)) => Err(err),
389 Outcome::Child(Ok(child)) => Ok(child.privileged_action_result),
390 Outcome::Child(Err(err)) => Err(err),
391 }
392 }
393 }
394
395 pub unsafe fn execute(self) -> Outcome<T> {
402 unsafe {
403 match perform_fork() {
404 Ok(Some(first_child_pid)) => Outcome::Parent(match waitpid(first_child_pid) {
405 Err(err) => Err(err.into()),
406 Ok(first_child_exit_status) => Ok(Parent {
407 first_child_exit_status,
408 }),
409 }),
410 Err(err) => Outcome::Parent(Err(err.into())),
411 Ok(None) => match self.execute_child() {
412 Ok(privileged_action_result) => Outcome::Child(Ok(Child {
413 privileged_action_result,
414 })),
415 Err(err) => Outcome::Child(Err(err.into())),
416 },
417 }
418 }
419 }
420
421 unsafe fn execute_child(self) -> Result<T, ErrorKind> {
422 unsafe {
423 if let Some(directory) = &self.directory {
424 set_current_dir(directory)
425 .map_err(|_| ErrorKind::ChangeDirectory(errno::errno().into()))?;
426 }
427
428 set_sid()?;
429 libc::umask(self.umask.inner);
430
431 if perform_fork()?.is_some() {
432 exit(0)
433 };
434
435 let pid_file_fd = self
436 .pid_file
437 .clone()
438 .map(|pid_file| create_pid_file(pid_file))
439 .transpose()?;
440
441 redirect_standard_streams(self.stdin, self.stdout, self.stderr)?;
442
443 let uid = self.user.map(get_user).transpose()?;
444 let gid = self.group.map(get_group).transpose()?;
445
446 let args: Option<(PathBuf, libc::uid_t, libc::gid_t)> = if let Some(pid) = self.pid_file
447 {
448 match (
449 self.chown_pid_file_user.map(get_user).transpose()?,
450 self.chown_pid_file_group.map(get_group).transpose()?,
451 ) {
452 (Some(uid), Some(gid)) => Some((pid, uid, gid)),
453 (None, Some(gid)) => Some((pid, libc::uid_t::MAX - 1, gid)),
454 (Some(uid), None) => Some((pid, uid, libc::gid_t::MAX - 1)),
455 _ => None,
456 }
457 } else {
458 None
459 };
460
461 if let Some((pid, uid, gid)) = args {
462 chown_pid_file(pid, uid, gid)?;
463 }
464
465 if let Some(pid_file_fd) = pid_file_fd {
466 set_cloexec_pid_file(pid_file_fd)?;
467 }
468
469 let privileged_action_result = (self.privileged_action)();
470
471 if let Some(root) = self.root {
472 change_root(root)?;
473 }
474
475 if let Some(gid) = gid {
476 set_group(gid)?;
477 }
478
479 if let Some(uid) = uid {
480 set_user(uid)?;
481 }
482
483 if let Some(pid_file_fd) = pid_file_fd {
484 write_pid_file(pid_file_fd)?;
485 }
486
487 Ok(privileged_action_result)
488 }
489 }
490}
491
492unsafe fn perform_fork() -> Result<Option<libc::pid_t>, ErrorKind> {
493 let pid = check_err(unsafe { libc::fork() }, ErrorKind::Fork)?;
494 if pid == 0 { Ok(None) } else { Ok(Some(pid)) }
495}
496
497unsafe fn waitpid(pid: libc::pid_t) -> Result<ExitStatus, ErrorKind> {
498 let mut status = 0;
499 check_err(
500 unsafe { libc::waitpid(pid, &mut status, 0) },
501 ErrorKind::Wait,
502 )?;
503 Ok(ExitStatus::from_raw(status))
504}
505
506unsafe fn set_sid() -> Result<(), ErrorKind> {
507 check_err(unsafe { libc::setsid() }, ErrorKind::DetachSession)?;
508 Ok(())
509}
510
511unsafe fn redirect_standard_streams(
512 stdin: Stdio,
513 stdout: Stdio,
514 stderr: Stdio,
515) -> Result<(), ErrorKind> {
516 let devnull_fd = check_err(
517 unsafe { libc::open(b"/dev/null\0" as *const [u8; 10] as _, libc::O_RDWR) },
518 ErrorKind::OpenDevnull,
519 )?;
520
521 let process_stdio = |fd, stdio: Stdio| {
522 match stdio.inner {
523 StdioImpl::Devnull => {
524 check_err(
525 unsafe { libc::dup2(devnull_fd, fd) },
526 ErrorKind::RedirectStreams,
527 )?;
528 }
529 StdioImpl::RedirectToFile(file) => {
530 let raw_fd = file.as_raw_fd();
531 check_err(
532 unsafe { libc::dup2(raw_fd, fd) },
533 ErrorKind::RedirectStreams,
534 )?;
535 }
536 StdioImpl::RedirectToFd(owned_fd) => {
537 check_err(
538 unsafe { libc::dup2(owned_fd.as_raw_fd(), fd) },
539 ErrorKind::RedirectStreams,
540 )?;
541 }
542 StdioImpl::Keep => (),
543 };
544 Ok(())
545 };
546
547 process_stdio(libc::STDIN_FILENO, stdin)?;
548 process_stdio(libc::STDOUT_FILENO, stdout)?;
549 process_stdio(libc::STDERR_FILENO, stderr)?;
550
551 check_err(unsafe { libc::close(devnull_fd) }, ErrorKind::CloseDevnull)?;
552
553 Ok(())
554}
555
556fn get_group(group: Group) -> Result<libc::gid_t, ErrorKind> {
557 match group.inner {
558 GroupImpl::Id(id) => Ok(id),
559 GroupImpl::Name(name) => {
560 let s = CString::new(name).map_err(|_| ErrorKind::GroupContainsNul)?;
561 match get_gid_by_name(&s) {
562 Some(id) => get_group(id.into()),
563 None => Err(ErrorKind::GroupNotFound),
564 }
565 }
566 }
567}
568
569unsafe fn set_group(group: libc::gid_t) -> Result<(), ErrorKind> {
570 check_err(unsafe { libc::setregid(group, group) }, ErrorKind::SetGroup)?;
571 Ok(())
572}
573
574fn get_user(user: User) -> Result<libc::uid_t, ErrorKind> {
575 match user.inner {
576 UserImpl::Id(id) => Ok(id),
577 UserImpl::Name(name) => {
578 let s = CString::new(name).map_err(|_| ErrorKind::UserContainsNul)?;
579 match get_uid_by_name(&s) {
580 Some(id) => get_user(id.into()),
581 None => Err(ErrorKind::UserNotFound),
582 }
583 }
584 }
585}
586
587unsafe fn set_user(user: libc::uid_t) -> Result<(), ErrorKind> {
588 check_err(unsafe { libc::setreuid(user, user) }, ErrorKind::SetUser)?;
589 Ok(())
590}
591
592unsafe fn create_pid_file(path: PathBuf) -> Result<libc::c_int, ErrorKind> {
593 let path_c = pathbuf_into_cstring(path)?;
594
595 unsafe {
596 let fd = check_err(
597 libc::open(path_c.as_ptr(), libc::O_WRONLY | libc::O_CREAT, 0o666),
598 ErrorKind::OpenPidfile,
599 )?;
600
601 check_err(
602 libc::flock(fd, libc::LOCK_EX | libc::LOCK_NB),
603 ErrorKind::LockPidfile,
604 )?;
605
606 Ok(fd)
607 }
608}
609
610fn chown_pid_file(path: PathBuf, uid: libc::uid_t, gid: libc::gid_t) -> Result<(), ErrorKind> {
611 let path_c = pathbuf_into_cstring(path)?;
612 check_err(
613 unsafe { libc::chown(path_c.as_ptr(), uid, gid) },
614 ErrorKind::ChownPidfile,
615 )?;
616 Ok(())
617}
618
619unsafe fn write_pid_file(fd: libc::c_int) -> Result<(), ErrorKind> {
620 let pid = unsafe { libc::getpid() };
621 let pid_buf = format!("{}\n", pid).into_bytes();
622 let pid_length = pid_buf.len();
623 let pid_c = CString::new(pid_buf).unwrap();
624
625 let written = unsafe {
626 check_err(libc::ftruncate(fd, 0), ErrorKind::TruncatePidfile)?;
627 check_err(
628 libc::write(fd, pid_c.as_ptr() as *const libc::c_void, pid_length),
629 ErrorKind::WritePid,
630 )?
631 };
632
633 if written < pid_length as isize {
634 return Err(ErrorKind::WritePidUnspecifiedError);
635 }
636
637 Ok(())
638}
639
640unsafe fn set_cloexec_pid_file(fd: libc::c_int) -> Result<(), ErrorKind> {
641 unsafe {
642 if cfg!(not(target_os = "redox")) {
643 let flags = check_err(libc::fcntl(fd, libc::F_GETFD), ErrorKind::GetPidfileFlags)?;
644
645 check_err(
646 libc::fcntl(fd, libc::F_SETFD, flags | libc::FD_CLOEXEC),
647 ErrorKind::SetPidfileFlags,
648 )?;
649 } else {
650 check_err(libc::ioctl(fd, libc::FIOCLEX), ErrorKind::SetPidfileFlags)?;
651 }
652 Ok(())
653 }
654}
655
656fn change_root(path: PathBuf) -> Result<(), ErrorKind> {
657 let path_c = pathbuf_into_cstring(path)?;
658 check_err(unsafe { libc::chroot(path_c.as_ptr()) }, ErrorKind::Chroot)?;
659 Ok(())
660}
661
662fn get_gid_by_name(name: &CStr) -> Option<libc::gid_t> {
663 unsafe {
664 let ptr = libc::getgrnam(name.as_ptr() as *const libc::c_char);
665 if ptr.is_null() {
666 None
667 } else {
668 let s = &*ptr;
669 Some(s.gr_gid)
670 }
671 }
672}
673
674fn get_uid_by_name(name: &CStr) -> Option<libc::uid_t> {
675 unsafe {
676 let ptr = libc::getpwnam(name.as_ptr() as *const libc::c_char);
677 if ptr.is_null() {
678 None
679 } else {
680 let s = &*ptr;
681 Some(s.pw_uid)
682 }
683 }
684}
685
686fn pathbuf_into_cstring(path: PathBuf) -> Result<CString, ErrorKind> {
687 CString::new(path.into_os_string().into_vec()).map_err(|_| ErrorKind::PathContainsNul)
688}