Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Limit Daemon Concurrency on Windows #1322

Merged
merged 9 commits into from
Feb 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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