Don't allow 2 processes of leanshot to run at the same time, using a

lockfile.
This commit is contained in:
Michael Zhang 2021-02-21 06:52:09 -06:00
parent b75eeae98c
commit b245c362c4
Signed by: michael
GPG key ID: BDA47A31A3C8EE6B
5 changed files with 87 additions and 4 deletions

2
Cargo.lock generated
View file

@ -172,7 +172,7 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "leanshot"
version = "0.4.1"
version = "0.5.0"
dependencies = [
"anyhow",
"image",

View file

@ -1,7 +1,7 @@
[package]
name = "leanshot"
description = "Screenshot capture for Linux."
version = "0.4.1"
version = "0.5.0"
repository = "https://git.mzhang.io/michael/leanshot"
license-file = "LICENSE"
edition = "2018"

View file

@ -223,7 +223,12 @@ impl Gui {
match evt.response_type() {
xcb::KEY_RELEASE => {
let evt = unsafe { xcb::cast_event::<xcb::KeyReleaseEvent>(&evt) };
trace!("key released: root={} key={} state={}", evt.root(), evt.detail(), evt.state());
trace!(
"key released: root={} key={} state={}",
evt.root(),
evt.detail(),
evt.state()
);
if evt.detail() == 9 {
if state.dragging {
@ -255,7 +260,7 @@ impl Gui {
// left mouse button
if state.dragging && evt.detail() == 1 {
state.dragging = false;
state.dragging = false;
if let Some(_) = state.rect() {
break;
}

View file

@ -4,6 +4,7 @@ extern crate log;
extern crate anyhow;
mod gui;
mod singleton;
use std::path::PathBuf;
use std::process;
@ -31,6 +32,10 @@ fn main() -> Result<()> {
capture.save_to(&opt.outfile)?;
}
Region::Selection => {
info!("Creating lockfile...");
let _lockfile = singleton::check_singleton()?;
info!("Lockfile: {:?}", _lockfile);
if let Some(rectangle) = gui.interactive_select(&capture)? {
capture.save_cropped_to(&opt.outfile, Some(rectangle))?;
} else {

73
src/singleton.rs Normal file
View file

@ -0,0 +1,73 @@
use std::fs::{File, self};
use std::io::{Write, Read};
use std::path::PathBuf;
use std::time::{SystemTime, Duration, UNIX_EPOCH};
use std::process;
use anyhow::{Error, Context, Result};
const PATHS: &[&str] = &["/var/run", "/tmp"];
pub fn check_singleton() -> Result<Lockfile> {
let (mut file, path) = get_lock_file()?;
let current_time = SystemTime::now().duration_since(UNIX_EPOCH)?.as_millis();
let pid = process::id();
let contents = format!("{}:{}", current_time, pid).as_bytes().to_vec();
file.write_all(&contents)?;
Ok(Lockfile(path))
}
#[derive(Debug)]
pub struct Lockfile(PathBuf);
impl Drop for Lockfile {
fn drop(&mut self) {
info!("Dropping lockfile...");
fs::remove_file(&self.0).unwrap();
}
}
fn get_lock_file() -> Result<(File, PathBuf)> {
for dir_path in PATHS.iter() {
use std::io::ErrorKind::*;
let dir_path = PathBuf::from(dir_path);
let lockfile_path = dir_path.join("leanshot.pid");
// check if it already exists
match File::open(&lockfile_path) {
Ok(mut f) => {
let mut contents = String::new();
f.read_to_string(&mut contents)?;
let mut parts = contents.split(":");
let timestamp = parts.next().unwrap().parse::<u64>()?;
let pid = parts.next().unwrap();
let time = UNIX_EPOCH + Duration::from_millis(timestamp);
// if it hasn't been 5 minutes since the last open, then bail
if SystemTime::now() - Duration::from_secs(60 * 5) < time {
bail!("existing lockfile found for pid {} at time {:?}", pid, time);
}
}
// ignore errors
Err(e) => match e.kind() {
NotFound => {},
_ => return Err(Error::from(e).context("could not open lockfile for reading")),
}
};
let file = match File::create(&lockfile_path) {
Ok(f) => f,
Err(e) => match e.kind() {
PermissionDenied => continue,
_ => return Err(Error::from(e).context("could not open lockfile for writing")),
},
};
return Ok((file, lockfile_path));
}
bail!("could not find a suitable place to place lockfile");
}