Skip to content

Instantly share code, notes, and snippets.

@FreeMasen
Last active September 25, 2018 05:04
Show Gist options
  • Save FreeMasen/909f2ed04cab2bd960ea68714e9199b6 to your computer and use it in GitHub Desktop.
Save FreeMasen/909f2ed04cab2bd960ea68714e9199b6 to your computer and use it in GitHub Desktop.

Revisions

  1. FreeMasen revised this gist Sep 21, 2018. No changes.
  2. FreeMasen created this gist Sep 21, 2018.
    7 changes: 7 additions & 0 deletions Cargo.toml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,7 @@
    [package]
    name = "getpid"
    version = "0.1.0"

    [dependencies]
    walkdir = "1"
    docopt = "2"
    12,148 changes: 12,148 additions & 0 deletions bindings.rs
    12,148 additions, 0 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
    80 changes: 80 additions & 0 deletions main.rs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,80 @@
    extern crate docopt;
    #[cfg(not(target_os = "macos"))]
    extern crate walkdir;
    #[cfg(not(target_os = "macos"))]
    use walkdir::Walkdir;
    #[cfg(not(target_os = "macos"))]
    use docopt::DocOpt;

    #[cfg(target_os = "macos")]
    mod bindings;
    #[cfg(target_os = "macos")]
    mod sysctl;

    #[cfg(target_os = "macos")]
    use sysctl::get_processes;

    static HELP: &str = "
    GET PID a tool for getting a pid for a running process.DocError
    Usage:
    getpid <name>
    getpid [--help|-h]
    Options:
    name The name of the executable running
    --help -h print this message
    ";

    #[derive(Deserialize)]
    struct Args {
    arg_name: String,
    }

    fn main() -> Result<(), Error> {
    //parse the command line arguments
    //and exit with help text if fails
    let args: Args = Docopt::new(HELP)
    .and_then(|d| d.deserialize())
    .unwrap_or_else(|e| e.exit());
    //if the arg is an empty string
    //show the help text and exit
    if args.arg_name == String::new() {
    println!("{}", HELP);
    ::std::process::exit(0);
    }
    //get the matches and keep only those
    //with a matching name
    let matches = get_processes().filter(|p| p.1 == args.arg_name);
    if matches.len() < 1 {
    //if no matches were found
    //print this message to stderr
    eprintln!("No process found")
    } else if matches.len() > 1 {
    //if more than one match was found
    //print this message to stderr
    eprintln!("More than one process with that name");
    } else {
    println!("{}", matches[0].0)
    }
    }

    #[cfg(not(target_os = "macos"))]
    fn get_processes() -> Vec<(String, String)> {
    WalkDir::new("/proc").min_depth(1).max_depth(1).filter_map(process_entry).collect()
    }
    #[cfg(not(target_os = "macos"))]
    fn process_entry(entry: Result<Entry, walkdir::Error>) -> Option<(String, String)> {
    //pull the actual entry out of the result
    //returning None if it fails
    let entry = entry.ok()?;
    //if the entry isn't a directory, skip it
    if !entry.file_type().is_dir() {
    return None;
    }
    //try and convert the file name from an OsStr into an &str
    //returning None if it fails
    let pid = entry.file_name().to_str().ok()?.to_string();
    //read the contents of the comm file to a string
    //returning None if it fails
    let comm = ::std::io::read_to_string(entry.path().join("comm")).ok()?;
    Some((pid, comm))
    }
    85 changes: 85 additions & 0 deletions sysctl.rs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,85 @@
    #![cfg(target_os = "macos")]
    use bindings::{kinfo_proc, sysctl};

    pub fn get_processes() -> Vec<(String, String)> {
    // this resolves in sysctl to kern.procs.all
    let mut name: [i32; 4] = [1, 14, 0, 0];
    // We will need this a few times to into a variable
    let name_len: u32 = 4;
    // This will be the length of bytes returned
    //for our Vec
    let mut len = 0;
    let mut err: i32 = unsafe {
    sysctl(
    //First argument is the name as a pointer
    name.as_mut_ptr(),
    //Next is the length of that name
    name_len,
    //Here we are sending a pointer to NULL
    ::std::ptr::null_mut(),
    //This value will hold our return value
    &mut len,
    //Here we are again sending a pointer to NULL
    ::std::ptr::null_mut(),
    //The last argument is 0
    0
    )
    };
    //if this is > 0 we have encountered an error
    if err > 0 {
    eprintln!("Error getting length of list: {}", err);
    return vec![];
    }
    //This should be the number of elements returned
    let expecting = len / ::std::mem::size_of::<kinfo_proc>();
    //This is the shape our vec
    let layout = ::std::alloc::Layout::new::<Vec<kinfo_proc>>();
    //Allocate the raw memory for our vec
    let ptr = unsafe {
    ::std::alloc::alloc_zeroed(layout)
    };
    let mut list: Vec<kinfo_proc> = unsafe {
    //Now create the vec with our length and capacity both set to the
    //calculation we did before
    Vec::from_raw_parts(ptr as *mut kinfo_proc, expecting, expecting)
    };
    err = unsafe {
    sysctl(
    //the same name
    name.as_mut_ptr(),
    //the same length
    name_len,
    //We swapped a null pointer for a pointer to our
    // Vec, cast as void *
    list.as_mut_ptr() as *mut ::std::os::raw::c_void,
    // This will get repopulated
    &mut len,
    //the same null pointer
    ::std::ptr::null_mut(),
    //again 0
    0,
    )
    };
    if err != 0 {
    eprintln!("Error getting kinfo_proc list: {}", err);
    return vec![];
    }

    list.iter().map(|p| {
    let name = parse_c_str(&p.kp_proc.p_comm);
    let pid = format!("{}", p.kp_proc.p_pid);
    (pid, name)
    }).collect()
    }

    fn parse_c_str(c_str: &[i8]) -> String {
    let mut bytes = vec![];
    for byte in c_str {
    if *byte as u8 == '\u{0}' as u8 {
    break;
    } else {
    bytes.push(*byte as u8);
    }
    }
    String::from_utf8_lossy(&bytes).to_string()
    }
    1 change: 1 addition & 0 deletions wrapper.c
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    #include <sys/sysctl>