diff --git a/.gitignore b/.gitignore index e1b2934..8e46064 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,9 @@ dist **/node_modules src-tauri/target/ **/creddy.db +# .env is system-specific +.env +.vscode # just in case credentials* diff --git a/src-tauri/.env b/src-tauri/.env deleted file mode 100644 index da27150..0000000 --- a/src-tauri/.env +++ /dev/null @@ -1 +0,0 @@ -DATABASE_URL=sqlite://C:/Users/Joe/AppData/Roaming/creddy/creddy.dev.db diff --git a/src-tauri/src/cli.rs b/src-tauri/src/cli.rs index c4b8b86..c4c1b2d 100644 --- a/src-tauri/src/cli.rs +++ b/src-tauri/src/cli.rs @@ -1,5 +1,6 @@ use std::ffi::OsString; use std::process::Command as ChildCommand; +#[cfg(windows)] use std::time::Duration; use clap::{ @@ -19,7 +20,6 @@ use crate::shortcuts::ShortcutAction; #[cfg(unix)] use { std::os::unix::process::CommandExt, - std::path::Path, tokio::net::UnixStream, }; @@ -199,7 +199,5 @@ async fn connect() -> Result { #[cfg(unix)] async fn connect() -> Result { - let path = Path::from("/tmp/creddy-requests"); - std::fs::remove_file(path)?; - UnixStream::connect(path) + UnixStream::connect("/tmp/creddy.sock").await } diff --git a/src-tauri/src/clientinfo.rs b/src-tauri/src/clientinfo.rs index 518809e..4cff73d 100644 --- a/src-tauri/src/clientinfo.rs +++ b/src-tauri/src/clientinfo.rs @@ -2,19 +2,6 @@ use std::path::{Path, PathBuf}; use sysinfo::{System, SystemExt, Pid, PidExt, ProcessExt}; use serde::{Serialize, Deserialize}; -use std::os::windows::io::AsRawHandle; - -#[cfg(windows)] -use { - tokio::net::windows::named_pipe::NamedPipeServer, - windows::Win32::{ - Foundation::HANDLE, - System::Pipes::GetNamedPipeClientProcessId, - }, -}; - -#[cfg(unix)] -use tokio::net::UnixStream; use crate::errors::*; @@ -26,25 +13,8 @@ pub struct Client { } -#[cfg(unix)] -pub fn get_client_parent(stream: &UnixStream) -> Result { - let pid = stream.peer_cred()?; - get_process_parent_info(pid)? -} - - -#[cfg(windows)] -pub fn get_client_parent(stream: &NamedPipeServer) -> Result { - let raw_handle = stream.as_raw_handle(); - let mut pid = 0u32; - let handle = HANDLE(raw_handle as _); - unsafe { GetNamedPipeClientProcessId(handle, &mut pid as *mut u32)? }; - - get_process_parent_info(pid) -} - - -fn get_process_parent_info(pid: u32) -> Result { +pub fn get_process_parent_info(pid: u32) -> Result { + dbg!(pid); let sys_pid = Pid::from_u32(pid); let mut sys = System::new(); sys.refresh_process(sys_pid); diff --git a/src-tauri/src/config.rs b/src-tauri/src/config.rs index c3d706b..4a9537e 100644 --- a/src-tauri/src/config.rs +++ b/src-tauri/src/config.rs @@ -4,11 +4,6 @@ use auto_launch::AutoLaunchBuilder; use is_terminal::IsTerminal; use serde::{Serialize, Deserialize}; use sqlx::SqlitePool; -use tauri::{ - Manager, - GlobalShortcutManager, - async_runtime as rt, -}; use crate::errors::*; @@ -183,43 +178,6 @@ fn default_hotkey_config() -> HotkeysConfig { } } -// note: will panic if called before APP is set -pub fn register_hotkeys(hotkeys: &HotkeysConfig) -> tauri::Result<()> { - let app = crate::app::APP.get().unwrap(); - - let mut manager = app.global_shortcut_manager(); - manager.unregister_all()?; - - if hotkeys.show_window.enabled { - let handle = app.app_handle(); - manager.register( - &hotkeys.show_window.keys, - move || { - handle.get_window("main") - .map(|w| w.show().error_popup("Failed to show")) - .ok_or(HandlerError::NoMainWindow) - .error_popup("No main window"); - }, - )?; - } - - if hotkeys.launch_terminal.enabled { - // register() doesn't take an async fn, so we have to use spawn - manager.register( - &hotkeys.launch_terminal.keys, - || { - rt::spawn(async { - crate::terminal::launch(false) - .await - .error_popup("Failed to launch"); - }); - } - )?; - } - - Ok(()) -} - fn default_rehide_ms() -> u64 { 1000 } // start minimized and on login only in production mode diff --git a/src-tauri/src/errors.rs b/src-tauri/src/errors.rs index 537980d..de7c823 100644 --- a/src-tauri/src/errors.rs +++ b/src-tauri/src/errors.rs @@ -244,6 +244,7 @@ pub enum ClientInfoError { ParentPidNotFound, #[error("Found PID for parent process of client, but no corresponding process")] ParentProcessNotFound, + #[cfg(windows)] #[error("Could not determine PID of connected client")] WindowsError(#[from] windows::core::Error), #[error(transparent)] diff --git a/src-tauri/src/server.rs b/src-tauri/src/server/mod.rs similarity index 63% rename from src-tauri/src/server.rs rename to src-tauri/src/server/mod.rs index 0afa5c6..e1f8463 100644 --- a/src-tauri/src/server.rs +++ b/src-tauri/src/server/mod.rs @@ -1,18 +1,9 @@ -#[cfg(windows)] -use tokio::net::windows::named_pipe::{ - NamedPipeServer, - ServerOptions, -}; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::sync::oneshot; use serde::{Serialize, Deserialize}; -use tauri::{ - AppHandle, - Manager, - async_runtime as rt, -}; +use tauri::{AppHandle, Manager}; use crate::errors::*; use crate::clientinfo::{self, Client}; @@ -21,6 +12,20 @@ use crate::ipc::{Approval, AwsRequestNotification}; use crate::state::AppState; use crate::shortcuts::{self, ShortcutAction}; +#[cfg(windows)] +mod server_win; +#[cfg(windows)] +pub use server_win::Server; +#[cfg(windows)] +use server_win::Stream; + +#[cfg(unix)] +mod server_unix; +#[cfg(unix)] +pub use server_unix::Server; +#[cfg(unix)] +use server_unix::Stream; + #[derive(Serialize, Deserialize)] pub enum Request { @@ -38,53 +43,8 @@ pub enum Response { } -pub struct Server { - listener: tokio::net::windows::named_pipe::NamedPipeServer, - app_handle: AppHandle, -} - -impl Server { - pub fn start(app_handle: AppHandle) -> std::io::Result<()> { - let listener = ServerOptions::new() - .first_pipe_instance(true) - .create(r"\\.\pipe\creddy-requests")?; - - let srv = Server {listener, app_handle}; - rt::spawn(srv.serve()); - Ok(()) - } - - async fn serve(mut self) { - loop { - if let Err(e) = self.try_serve().await { - eprintln!("Error accepting connection: {e}"); - } - } - } - - async fn try_serve(&mut self) -> std::io::Result<()> { - // connect() just waits for a client to connect, it doesn't return anything - self.listener.connect().await?; - - // create a new pipe instance to listen for the next client, and swap it in - let new_listener = ServerOptions::new().create(r"\\.\pipe\creddy-requests")?; - let mut stream = std::mem::replace(&mut self.listener, new_listener); - let new_handle = self.app_handle.app_handle(); - rt::spawn(async move { - let res = serde_json::to_string( - &handle(&mut stream, new_handle).await - ).unwrap(); - if let Err(e) = stream.write_all(res.as_bytes()).await { - eprintln!("Error responding to request: {e}"); - } - }); - - Ok(()) - } -} - - -async fn handle(stream: &mut NamedPipeServer, app_handle: AppHandle) -> Result { +async fn handle(mut stream: Stream, app_handle: AppHandle, client_pid: u32) -> Result<(), HandlerError> +{ // read from stream until delimiter is reached let mut buf: Vec = Vec::with_capacity(1024); // requests are small, 1KiB is more than enough let mut n = 0; @@ -98,13 +58,17 @@ async fn handle(stream: &mut NamedPipeServer, app_handle: AppHandle) -> Result get_aws_credentials(base, client, app_handle).await, Request::InvokeShortcut(action) => invoke_shortcut(action).await, - } + }; + + let res = serde_json::to_vec(&res).unwrap(); + stream.write_all(&res).await?; + Ok(()) } diff --git a/src-tauri/src/server/server_unix.rs b/src-tauri/src/server/server_unix.rs new file mode 100644 index 0000000..bd3b4ff --- /dev/null +++ b/src-tauri/src/server/server_unix.rs @@ -0,0 +1,59 @@ +use std::io::ErrorKind; +use tokio::net::{UnixListener, UnixStream}; +use tauri::{ + AppHandle, + Manager, + async_runtime as rt, +}; + +use crate::errors::*; + + +pub type Stream = UnixStream; + + +pub struct Server { + listener: UnixListener, + app_handle: AppHandle, +} + +impl Server { + pub fn start(app_handle: AppHandle) -> std::io::Result<()> { + match std::fs::remove_file("/tmp/creddy.sock") { + Ok(_) => (), + Err(e) if e.kind() == ErrorKind::NotFound => (), + Err(e) => return Err(e), + } + + let listener = UnixListener::bind("/tmp/creddy.sock")?; + let srv = Server { listener, app_handle }; + rt::spawn(srv.serve()); + Ok(()) + } + + async fn serve(self) { + loop { + self.try_serve() + .await + .error_print_prefix("Error accepting request: "); + } + } + + async fn try_serve(&self) -> Result<(), HandlerError> { + let (stream, _addr) = self.listener.accept().await?; + let new_handle = self.app_handle.app_handle(); + let client_pid = get_client_pid(&stream)?; + rt::spawn(async move { + super::handle(stream, new_handle, client_pid) + .await + .error_print_prefix("Error responding to request: "); + }); + Ok(()) + } +} + + +fn get_client_pid(stream: &UnixStream) -> std::io::Result { + let cred = stream.peer_cred()?; + Ok(cred.pid().unwrap() as u32) +} diff --git a/src-tauri/src/server/server_win.rs b/src-tauri/src/server/server_win.rs new file mode 100644 index 0000000..4b25106 --- /dev/null +++ b/src-tauri/src/server/server_win.rs @@ -0,0 +1,76 @@ +use tokio::{ + net::windows::named_pipe::{ + NamedPipeServer, + ServerOptions, + }, + sync::oneshot, +}; + +#[cfg(windows)] +use windows::Win32:: { + Foundation::HANDLE, + System::Pipes::GetNamedPipeClientProcessId, +}; +#[cfg(windows)] +use std::os::windows::io::AsRawHandle; + +use tauri::async_runtime as rt; + +use crate::errors::*; + + +// used by parent module +pub type Stream = NamedPipeServer; + + +pub struct Server { + listener: NamedPipeServer, + app_handle: AppHandle, +} + +impl Server { + pub fn start(app_handle: AppHandle) -> std::io::Result<()> { + let listener = ServerOptions::new() + .first_pipe_instance(true) + .create(r"\\.\pipe\creddy-requests")?; + + let srv = Server {listener, app_handle}; + rt::spawn(srv.serve()); + Ok(()) + } + + async fn serve(mut self) { + loop { + if let Err(e) = self.try_serve().await { + eprintln!("Error accepting connection: {e}"); + } + } + } + + async fn try_serve(&mut self) -> Result<(), HandlerError> { + // connect() just waits for a client to connect, it doesn't return anything + self.listener.connect().await?; + + // create a new pipe instance to listen for the next client, and swap it in + let new_listener = ServerOptions::new().create(r"\\.\pipe\creddy-requests")?; + let mut stream = std::mem::replace(&mut self.listener, new_listener); + let new_handle = self.app_handle.app_handle(); + let client_pid = get_client_pid(&stream)?; + rt::spawn(async move { + super::handle(stream, app_handle) + .await + .error_print_prefix("Error responding to request: "); + }); + + Ok(()) + } +} + + +fn get_client_pid(pipe: &NamedPipeServer) -> Result { + let raw_handle = pipe.as_raw_handle(); + let mut pid = 0u32; + let handle = HANDLE(raw_handle as _); + unsafe { GetNamedPipeClientProcessId(handle, &mut pid as *mut u32)? }; + pid +}