Skip to content

Commit

Permalink
Merge pull request #1322 from zowe/next-daemon-file-lock-windows
Browse files Browse the repository at this point in the history
Limit Daemon Concurrency on Windows
  • Loading branch information
t1m0thyj authored Feb 24, 2022
2 parents 9ce8d8e + db72809 commit 0fbbe80
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 17 deletions.
1 change: 1 addition & 0 deletions packages/cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ All notable changes to the Zowe CLI package will be documented in this file.
- CLI plug-ins that read from `process.stdin` in their command handlers should replace it with `{IHandlerParameters}.stdin` to be compatible with Zowe v2 daemon mode.
- This may be a breaking change for unit tests that mock the `IHandlerParameters` interface since a required property has been added.
- It is recommended to replace `IHandlerParameters` mocks with the `mockHandlerParameters` method in the @zowe/cli-test-utils package which should protect you from future breaking changes to this interface.
- BugFix: Fixed Daemon Concurrency problems in Windows by introducing a lock file

## `7.0.0-next.202202171858`

Expand Down
13 changes: 12 additions & 1 deletion zowex/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion zowex/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "zowe"
version = "0.7.0"
version = "0.7.1"
authors = ["Zowe Project"]
edition = "2018"
license = "EPL-2.0"
Expand All @@ -20,4 +20,5 @@ sysinfo = "0.22.5"
whoami = "1.2.1"

[target.'cfg(windows)'.dependencies]
fslock = "0.2.1"
named_pipe = "0.4.1"
6 changes: 5 additions & 1 deletion zowex/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ In testing a solution, the root command tree takes longer to execute than lower

***This client should NOT be used in an environment where multiple individuals use the same system (i.e. a shared Linux server).***

Our native executable client communicates with the Zowe CLI persistent process (daemon) over named pipes on Windows, and Unix sockets on other operating systems. An environment variable can set the named pipe or Unix socket used by the daemon. The environment variable named `ZOWE_DAEMON=<PATH>` is used to specify the pipe's name or socket's location. If that variable is unset, the default is `<username>\ZoweDaemon` for Windows, and `<homedir>/.zowe-daemon.sock` on other operating systems.
Our native executable client communicates with the Zowe CLI persistent process (daemon) over named pipes on Windows, and Unix sockets on other operating systems.

An environment variable can set the named pipe or Unix socket used by the daemon. The environment variable named `ZOWE_DAEMON=<PATH>` is used to specify the pipe's name or socket's location. If that variable is unset, the default is `<username>\ZoweDaemon` for Windows, and `<homedir>/.zowe-daemon.sock` on other operating systems.

On Windows systems, a lockfile is used to prevent multiple concurrent requests to the daemon. By default, the lockfile is stored at `<homedir>\.zowe-daemon.lock`. The lockfile location can be overridden with the environment variable `ZOWE_DAEMON_LOCK`, and, if specified, should be set to an absolute path (i.e. `$env:ZOWE_DAEMON_LOCK = "C:\Users\user\.zowe\.zowe-daemon.lock"` for PowerShell, `set ZOWE_DAEMON_LOCK=C:\Users\user\.zowe\.zowe-daemon.lock` for Command Prompt).

## Enabling daemon-mode

Expand Down
77 changes: 63 additions & 14 deletions zowex/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,26 @@ use std::env;
use std::io;
use std::io::BufReader;
use std::io::prelude::*;
use std::net::Shutdown;
use std::process::{Command, Stdio};
use std::str;
use std::thread;
use std::time::Duration;

#[cfg(target_family = "unix")]
use std::os::unix::net::UnixStream;
#[cfg(target_family = "unix")]
use std::net::Shutdown;

#[cfg(target_family = "windows")]
extern crate named_pipe;
#[cfg(target_family = "windows")]
use named_pipe::PipeClient;
#[cfg(target_family = "windows")]
extern crate fslock;
#[cfg(target_family = "windows")]
use fslock::LockFile;
#[cfg(target_family = "windows")]
use std::fs::File;

extern crate atty;
use atty::Stream;
Expand All @@ -32,11 +44,6 @@ use base64::encode;
extern crate home;
use home::home_dir;

#[cfg(target_family = "windows")]
extern crate named_pipe;
#[cfg(target_family = "windows")]
use named_pipe::PipeClient;

extern crate pathsearch;
use pathsearch::PathSearcher;

Expand Down Expand Up @@ -292,7 +299,37 @@ fn run_daemon_command(args: &mut Vec<String>) -> io::Result<()> {

let mut tries = 0;
let socket_string = get_socket_string();
#[cfg(target_family = "windows")]
let mut lock_file;
#[cfg(target_family = "windows")]
match get_lock_file() {
Ok(result) => { lock_file = result; },
Err(_e) => { panic!("Could not find or create the lock file. Check your ZOWE_DAEMON_LOCK variable.")}
}
#[cfg(target_family = "windows")]
let mut locked = false;
loop {
#[cfg(target_family = "windows")]
if !locked {
match lock_file.try_lock() {
Ok(result) if !result => {
if tries > THREE_MIN_OF_RETRIES {
println!("Terminating after {} connection retries.", THREE_MIN_OF_RETRIES);
std::process::exit(EXIT_CODE_TIMEOUT_CONNECT_TO_RUNNING_DAEMON);
}

tries += 1;
println!("The Zowe daemon is in use, retrying ({} of {})", tries, THREE_MIN_OF_RETRIES);

// pause between attempts to connect
thread::sleep(Duration::from_secs(THREE_SEC_DELAY));
continue;
},
Ok(_result) => { locked = true; },
Err (ref e) => { panic!("Problem acquiring lock: {:?}", e) }
}
}

let mut stream = establish_connection(&socket_string)?;
match talk(&_resp, &mut stream) {
Err(ref e) if e.kind() == io::ErrorKind::ConnectionReset => {
Expand Down Expand Up @@ -528,6 +565,24 @@ fn get_socket_string() -> String {
_socket
}

#[cfg(target_family = "windows")]
fn get_lock_file() -> io::Result<LockFile> {
let lock_file_name = get_lock_string();
let _lock_file_created = File::create(&lock_file_name);
LockFile::open(&lock_file_name)
}

#[cfg(target_family = "windows")]
fn get_lock_string() -> String {
let mut _lock = format!("{}\\{}", home_dir().unwrap().to_string_lossy(), ".zowe-daemon.lock");

if let Ok(lock_name) = env::var("ZOWE_DAEMON_LOCK") {
_lock = lock_name;
}

_lock
}

// Get the file path to the command that runs the NodeJS version of Zowe
fn get_nodejs_zowe_path() -> String {
/* On Linux/Mac both our executable and shell script are named 'zowe'.
Expand Down Expand Up @@ -586,16 +641,10 @@ fn is_daemon_running() -> DaemonProcInfo {
let mut sys = System::new_all();
sys.refresh_all();
for (pid, process) in sys.processes() {
if (process.name().to_lowercase().contains("node") &&
process.cmd().len() > 2 &&
process.cmd()[1].to_lowercase().contains("@zowe") &&
process.cmd()[1].to_lowercase().contains("cli") &&
process.cmd()[2].to_lowercase() == "--daemon") ||
(process.name().to_lowercase().contains("node") &&
if process.name().to_lowercase().contains("node") &&
process.cmd().len() > 2 &&
process.cmd()[1].to_lowercase().contains("bin") &&
process.cmd()[1].to_lowercase().contains("zowe") &&
process.cmd()[2].to_lowercase() == "--daemon")
process.cmd()[2].to_lowercase() == "--daemon"
{
// convert the process command from a vector to a string
let mut proc_cmd: String = String::new();
Expand Down

0 comments on commit 0fbbe80

Please sign in to comment.