blob: d4721c1ffd9711c4996c87412f2a64c2aaaf946b [file] [log] [blame] [edit]
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::fmt::Display;
use std::fs::File;
use std::io;
use std::io::BufRead;
use std::io::BufReader;
/// Error of parsing /proc/pid/status
#[derive(Debug)]
pub enum Error {
NotFound(u32),
FileCorrupt,
Io(io::Error),
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::NotFound(_) => None,
Self::FileCorrupt => None,
Self::Io(e) => Some(e),
}
}
}
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::NotFound(pid) => f.write_fmt(format_args!("/proc/{pid}/status is not found")),
Self::FileCorrupt => f.write_str("/proc/pid/status invalid format"),
Self::Io(e) => f.write_fmt(format_args!("procfs: {e}")),
}
}
}
pub type Result<T> = std::result::Result<T, Error>;
enum UidIndex {
Real = 0,
Effective = 1,
}
/// Load real user id of the process from /proc/pid/status
pub fn load_ruid(pid: u32) -> Result<u32> {
let mut file = open_status_file(pid)?;
load_uid(&mut file, UidIndex::Real)
}
/// Load effective user id of the process from /proc/pid/status
pub fn load_euid(pid: u32) -> Result<u32> {
let mut file = open_status_file(pid)?;
load_uid(&mut file, UidIndex::Effective)
}
fn open_status_file(pid: u32) -> Result<File> {
File::open(format!("/proc/{}/status", pid)).map_err(|e| {
if e.kind() == io::ErrorKind::NotFound {
Error::NotFound(pid)
} else {
Error::Io(e)
}
})
}
fn load_uid(file: &mut File, i_uid: UidIndex) -> Result<u32> {
let r = BufReader::with_capacity(1024, file);
for line in r.lines() {
let line = line.map_err(Error::Io)?;
const UID_TAG: &str = "Uid:";
if let Some(uids) = line.strip_prefix(UID_TAG) {
return uids
.split_whitespace()
.nth(i_uid as usize)
.and_then(|uid| uid.parse().ok())
.ok_or(Error::FileCorrupt);
}
}
Err(Error::FileCorrupt)
}
#[cfg(test)]
mod tests {
use std::io::Seek;
use std::io::SeekFrom;
use std::io::Write;
use super::*;
use crate::proc::load_ruid;
#[test]
fn test_load_ruid() {
assert!(load_ruid(std::process::id()).is_ok());
assert!(matches!(
load_ruid(u32::MAX),
Err(Error::NotFound(u32::MAX))
));
}
#[test]
fn test_load_euid() {
assert!(load_euid(std::process::id()).is_ok());
assert!(matches!(
load_euid(u32::MAX),
Err(Error::NotFound(u32::MAX))
));
}
#[test]
fn test_load_uid() {
const STATUS_FILE: &str = "Name: resourced
Umask: 0022
State: S (sleeping)
Tgid: 12153
Ngid: 0
Pid: 12153
PPid: 1
TracerPid: 0
Uid: 20170 20171 20172 20173
Gid: 20174 20175 20176 20177";
let mut file = tempfile::tempfile().unwrap();
file.write_all(STATUS_FILE.as_bytes()).unwrap();
file.seek(SeekFrom::Start(0)).unwrap();
assert_eq!(load_uid(&mut file, UidIndex::Real).unwrap(), 20170);
file.seek(SeekFrom::Start(0)).unwrap();
assert_eq!(load_uid(&mut file, UidIndex::Effective).unwrap(), 20171);
}
}