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