1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
// License: see LICENSE file at root directory of `master` branch

//! # Namaste
//!
//! ## Project
//!
//! - Repository: <https://bitbucket.org/haibison/namaste-rs>
//! - License: Nice License 1.0.0 _(see LICENSE file at root directory of `master` branch)_
//! - _This project follows [Semantic Versioning 2.0.0]_
//!
//! ## Features
//!
//! - Handling locks amongst processes via TCP.
//!
//! ## Design
//!
//! ### Identifier
//!
//! An identifier - ID - is a 64-byte array, which matches an [SHA3-512][wiki:SHA3] hash.
//!
//! ### Server and client
//!
//! - Server starts on a fixed TCP port, and listens for clients.
//!
//! - "Book" command:
//!
//!     + When a new client connects to, they are expected to send 4 pieces of information:
//!
//!         + A one-byte "book" command.
//!         + Global ID. This ID is used within a lifetime of the server, as an identification of the client.
//!         + Handshake ID - _must_ be different to above ID. This ID is used for the server to communicate with client.
//!         + Client's local TCP server port, consisting of 2 bytes, in big-endian order.
//!
//!     + Server then connects to the client's local server, sends the handshake ID, reads an ID and compares it with the first one above.
//!
//!     + If the verification passes, server stores client ID in memory, and sends back _one non-zero byte_ to client.
//!
//!     + If the verification does _not_ passes, server sends back _one zero byte_ to client.
//!
//! - "Check out" command sends one-byte command, ID and handshake ID.
//!
//! ### Limits
//!
//! - Server stores all IDs in memory. So if it crashes, they are lost.
//! - Server relies on a fixed loop-back IP address ([`DEFAULT_IP`][::DEFAULT_IP]) as a protection against _external_
//!   [Denial-of-service attacks][wiki:DoS-attack]. However, it is **not** protected against attacks from internal programs running on a same
//!   machine.
//! - Client side will ask system to grant it a random TCP server port. On most systems, TCP ports are _limited_.
//!
//! About crashing issue: we always strive to code carefully. If you find out bugs, we are thankful if you file reports in project repository.
//!
//! ## Usage
//!
//! You should only rely on this library for _limited small amount_ of locks between your program's processes. For instance, this library can
//! help with single-instance designed programs.
//!
//! ## Frequently Answered Questions
//!
//! ### Why not Unix Domain Socket?
//!
//! UDS has a good design, but its implementation has some **serious** flaws:
//!
//! - Already-bound file path _can be deleted_. Any new binding to that path will _silently take control_ of current one.
//! - Linux has an extension to UDS, which is called _abstract sockets_. Its design is very good, and can totally replace this library. However
//!   its documentation clearly states that it is _not_ portable.
//!
//! ### Why [`Namaste`][::Namaste]?
//!
//! [`Namaste`][::Namaste] uses abstract Linux sockets underneath. It was made because, as stated above, abstract socket is a good design. Using
//! that struct requires `unsafe` blocks because it calls [`libc`][crate:libc].
//!
//! ### Why not file locks?
//!
//! - On Unix, locked files can still be deleted.
//!
//! ### Alternatives
//!
//! Currently the authors are not aware of any projects similar to this one.
//!
//! ## Building server
//!
//! - Via Cargo:
//!
//!     ```shell
//!     ~> cargo install namaste --version=x.y.z --features=bin
//!     ```
//!
//! - From source:
//!
//!     ```shell
//!     ~> # Clone a specific version via tag name
//!     ~> git clone --branch=x.y.z --depth=1 -- https://bitbucket.org/haibison/namaste-rs namaste-x.y.z/
//!     ~> cd namaste-x.y.z/
//!     ~> cargo build --release --features=bin
//!     ```
//!
//! ## Examples
//!
//! It is expected that the system _already_ has Namaste server running!
//!
//! ```ignore
//! use namaste::client::Client;
//!
//! // ID must be a constant.
//! const NAMASTE_ID: namaste::Id = [...];
//!
//! fn main() {
//!     // You should generate new handshake ID each time you call Client::book().
//!     let handshake_id = ...;
//!     match Client::book(NAMASTE_ID, handshake_id, None, namaste::DEFAULT_RW_TIMEOUT) {
//!         Ok(Some(client)) => {
//!             run_main_job();
//!             match client.check_out() {
//!                 Ok(true) => println!("Successfully checked out of Namaste server"),
//!                 Ok(false) => eprintln!("Couldn't check out of Namaste server"),
//!                 Err(err) => eprintln!("Failed checking out of Namaste server: {}", err),
//!             };
//!         },
//!         Ok(None) => eprintln!("Another instance is running"),
//!         Err(err) => eprintln!("Failed connecting to Namaste server: {}", err),
//!     };
//! }
//! ```
//!
//! ## Other notes
//!
//! To generate an ID, you can get help from [Dia-Hammer][crate:dia-hammer]:
//!
//! ```shell
//! $ hammer sha3-512 --limit=65536 --format=hex-array -- /dev/urandom
//! ```
//!
//! To generate a handshake ID, you can use [`tiny-keccak`][crate:tiny-keccak].
//!
//! [Semantic Versioning 2.0.0]: https://semver.org/spec/v2.0.0.html
//!
//! [wiki:SHA3]: https://en.wikipedia.org/wiki/SHA-3
//! [wiki:DoS-attack]: https://en.wikipedia.org/wiki/Denial-of-service_attack
//!
//! [::DEFAULT_IP]: constant.DEFAULT_IP.html
//! [::Namaste]: struct.Namaste.html
//!
//! [crate:dia-hammer]: https://bitbucket.org/haibison/hammer
//! [crate:libc]: https://crates.io/crates/libc
//! [crate:tiny-keccak]: https://crates.io/crates/tiny-keccak

#[macro_use]
#[allow(unused_macros)]
mod __;
mod root;

pub use root::*;

pub mod client;
pub mod server;
pub mod version_info;

// ╔═════════════════╗
// ║   IDENTIFIERS   ║
// ╚═════════════════╝

macro_rules! code_name  { () => { "namaste" }}
macro_rules! version    { () => { "0.12.0" }}

/// # Crate name
pub const NAME: &str = "Namaste";

/// # Crate code name
pub const CODE_NAME: &str = code_name!();

/// # ID of this crate
pub const ID: &str = concat!(
    "b1b4db58-59414682-a029819c-84b9a4e1-170e0050-92722253-fae9dfd6-b652a960-",
    "e553656c-6b625519-392bb87b-c23a6af3-c498c567-ff5c242f-606d2694-9113a891",
);

/// # Crate version
pub const VERSION: &str = version!();

/// # Crate release date (year/month/day)
pub const RELEASE_DATE: (u16, u8, u8) = (2019, 7, 8);

/// # Tag, which can be used for logging...
pub const TAG: &str = concat!(code_name!(), "::b1b4db58::", version!());

// ╔════════════════════╗
// ║   IMPLEMENTATION   ║
// ╚════════════════════╝

#[cfg(feature="abstract-linux-sockets")]
mod namaste;

#[cfg(feature="abstract-linux-sockets")]
pub use crate::namaste::*;

/// # Result type used in this crate
pub type Result<T> = std::io::Result<T>;

#[test]
fn test_crate_version() {
    assert_eq!(VERSION, env!("CARGO_PKG_VERSION"));
}