Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2572052
mod.rs
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
16 KB
Referenced Files
None
Subscribers
None
mod.rs
View Options
mod
unix
;
use
crate
::{
config
,
config
::
structs
::
Server
,
file
,
helpers
,
service
::{
run
,
stop
,
ProcessMetadata
},
};
use
std
::{
env
,
path
::
PathBuf
,
sync
::{
Arc
,
Mutex
},
};
use
nix
::{
sys
::
signal
::{
kill
,
Signal
},
unistd
::
Pid
,
};
use
chrono
::
serde
::
ts_milliseconds
;
use
chrono
::{
DateTime
,
Utc
};
use
global_placeholders
::
global
;
use
macros_rs
::{
crashln
,
string
,
ternary
,
then
};
use
psutil
::
process
;
use
serde
::{
Deserialize
,
Serialize
};
use
std
::
collections
::
BTreeMap
;
use
utoipa
::
ToSchema
;
#[derive(Serialize, Deserialize, ToSchema)]
pub
struct
ItemSingle
{
pub
info
:
Info
,
pub
stats
:
Stats
,
pub
watch
:
Watch
,
pub
log
:
Log
,
pub
raw
:
Raw
,
}
#[derive(Serialize, Deserialize, ToSchema)]
pub
struct
Info
{
pub
id
:
usize
,
pub
pid
:
i64
,
pub
name
:
String
,
pub
status
:
String
,
#[schema(value_type = String, example =
"/path"
)]
pub
path
:
PathBuf
,
pub
uptime
:
String
,
pub
command
:
String
,
pub
children
:
Vec
<
i64
>
,
}
#[derive(Serialize, Deserialize, ToSchema)]
pub
struct
Stats
{
pub
restarts
:
u64
,
pub
start_time
:
i64
,
pub
cpu_percent
:
Option
<
f32
>
,
pub
memory_usage
:
Option
<
MemoryInfo
>
,
}
#[derive(Serialize, Deserialize, ToSchema)]
pub
struct
MemoryInfo
{
pub
rss
:
u64
,
pub
vms
:
u64
,
}
#[derive(Serialize, Deserialize, ToSchema)]
pub
struct
Log
{
pub
out
:
String
,
pub
error
:
String
,
}
#[derive(Serialize, Deserialize, ToSchema)]
pub
struct
Raw
{
pub
running
:
bool
,
pub
crashed
:
bool
,
pub
crashes
:
u64
,
}
#[derive(Clone)]
pub
struct
LogInfo
{
pub
out
:
String
,
pub
error
:
String
,
}
#[derive(Serialize, Deserialize, ToSchema)]
pub
struct
ProcessItem
{
pid
:
i64
,
id
:
usize
,
cpu
:
String
,
mem
:
String
,
name
:
String
,
restarts
:
u64
,
status
:
String
,
uptime
:
String
,
#[schema(example =
"/path"
)]
watch_path
:
String
,
#[schema(value_type = String, example =
"2000-01-01T01:00:00.000Z"
)]
start_time
:
DateTime
<
Utc
>
,
}
#[derive(Clone)]
pub
struct
ProcessWrapper
{
pub
id
:
usize
,
pub
runner
:
Arc
<
Mutex
<
Runner
>>
,
}
type
Env
=
BTreeMap
<
String
,
String
>
;
#[derive(Clone, Debug, Deserialize, Serialize)]
pub
struct
Process
{
pub
id
:
usize
,
pub
pid
:
i64
,
pub
env
:
Env
,
pub
name
:
String
,
pub
path
:
PathBuf
,
pub
script
:
String
,
pub
restarts
:
u64
,
pub
running
:
bool
,
pub
crash
:
Crash
,
pub
watch
:
Watch
,
pub
children
:
Vec
<
i64
>
,
#[serde(with =
"ts_milliseconds"
)]
pub
started
:
DateTime
<
Utc
>
,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub
struct
Crash
{
pub
crashed
:
bool
,
pub
value
:
u64
,
}
#[derive(Clone, Debug, Deserialize, Serialize, ToSchema)]
pub
struct
Watch
{
pub
enabled
:
bool
,
#[schema(example =
"/path"
)]
pub
path
:
String
,
pub
hash
:
String
,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub
struct
Runner
{
pub
id
:
id
::
Id
,
#[serde(skip)]
pub
remote
:
Option
<
Remote
>
,
pub
list
:
BTreeMap
<
usize
,
Process
>
,
}
#[derive(Clone, Debug)]
pub
struct
Remote
{
address
:
String
,
token
:
Option
<
String
>
,
pub
config
:
RemoteConfig
,
}
#[derive(Clone, Debug, Deserialize)]
pub
struct
RemoteConfig
{
pub
shell
:
String
,
pub
args
:
Vec
<
String
>
,
pub
log_path
:
String
,
}
pub
enum
Status
{
Offline
,
Running
,
}
impl
Status
{
pub
fn
to_bool
(
&
self
)
->
bool
{
match
self
{
Status
::
Offline
=>
false
,
Status
::
Running
=>
true
,
}
}
}
macro_rules!
lock
{
(
$runner
:
expr
)
=>
{{
match
$runner
.
lock
()
{
Ok
(
runner
)
=>
runner
,
Err
(
err
)
=>
crashln
!
(
"Unable to lock mutex: {err}"
),
}
}};
}
fn
kill_children
(
children
:
Vec
<
i64
>
)
{
for
pid
in
children
{
if
let
Err
(
err
)
=
kill
(
Pid
::
from_raw
(
pid
as
i32
),
Signal
::
SIGTERM
)
{
log
::
error
!
(
"Failed to stop pid {pid}: {err:?}"
);
};
}
}
impl
Runner
{
pub
fn
new
()
->
Self
{
dump
::
read
()
}
pub
fn
connect
(
name
:
String
,
Server
{
address
,
token
}:
Server
,
verbose
:
bool
)
->
Option
<
Self
>
{
let
remote_config
=
match
config
::
from
(
&
address
,
token
.
as_deref
())
{
Ok
(
config
)
=>
config
,
Err
(
err
)
=>
{
log
::
error
!
(
"{err}"
);
return
None
;
}
};
if
let
Ok
(
dump
)
=
dump
::
from
(
&
address
,
token
.
as_deref
())
{
then
!
(
verbose
,
println!
(
"{} Fetched remote (name={name}, address={address})"
,
*
helpers
::
SUCCESS
));
Some
(
Runner
{
remote
:
Some
(
Remote
{
token
,
address
:
string
!
(
address
),
config
:
remote_config
,
}),
..
dump
})
}
else
{
None
}
}
pub
fn
start
(
&
mut
self
,
name
:
&
String
,
command
:
&
String
,
path
:
PathBuf
,
watch
:
&
Option
<
String
>
)
->
&
mut
Self
{
if
let
Some
(
remote
)
=
&
self
.
remote
{
if
let
Err
(
err
)
=
http
::
create
(
remote
,
name
,
command
,
path
,
watch
)
{
crashln
!
(
"{} Failed to start create {name}
\n
Error: {:#?}"
,
*
helpers
::
FAIL
,
err
);
};
}
else
{
let
id
=
self
.
id
.
next
();
let
config
=
config
::
read
().
runner
;
let
crash
=
Crash
{
crashed
:
false
,
value
:
0
};
let
watch
=
match
watch
{
Some
(
watch
)
=>
Watch
{
enabled
:
true
,
path
:
string
!
(
watch
),
hash
:
hash
::
create
(
file
::
cwd
().
join
(
watch
)),
},
None
=>
Watch
{
enabled
:
false
,
path
:
string
!
(
""
),
hash
:
string
!
(
""
),
},
};
let
pid
=
run
(
ProcessMetadata
{
args
:
config
.
args
,
name
:
name
.
clone
(),
shell
:
config
.
shell
,
command
:
command
.
clone
(),
log_path
:
config
.
log_path
,
env
:
unix
::
env
(),
});
self
.
list
.
insert
(
id
,
Process
{
id
,
pid
,
path
,
watch
,
crash
,
restarts
:
0
,
running
:
true
,
children
:
vec
!
[],
name
:
name
.
clone
(),
started
:
Utc
::
now
(),
script
:
command
.
clone
(),
env
:
env
::
vars
().
collect
(),
},
);
}
return
self
;
}
pub
fn
restart
(
&
mut
self
,
id
:
usize
,
dead
:
bool
)
->
&
mut
Self
{
if
let
Some
(
remote
)
=
&
self
.
remote
{
if
let
Err
(
err
)
=
http
::
restart
(
remote
,
id
)
{
crashln
!
(
"{} Failed to start process {id}
\n
Error: {:#?}"
,
*
helpers
::
FAIL
,
err
);
};
}
else
{
let
process
=
self
.
process
(
id
);
let
config
=
config
::
read
().
runner
;
let
Process
{
path
,
script
,
name
,
..
}
=
process
.
clone
();
kill_children
(
process
.
children
.
clone
());
stop
(
process
.
pid
);
if
let
Err
(
err
)
=
std
::
env
::
set_current_dir
(
&
path
)
{
crashln
!
(
"{} Failed to set working directory {:?}
\n
Error: {:#?}"
,
*
helpers
::
FAIL
,
path
,
err
);
};
process
.
pid
=
run
(
ProcessMetadata
{
args
:
config
.
args
,
name
:
name
.
clone
(),
shell
:
config
.
shell
,
log_path
:
config
.
log_path
,
command
:
script
.
to_string
(),
env
:
unix
::
env
(),
});
process
.
running
=
true
;
process
.
children
=
vec!
[];
process
.
started
=
Utc
::
now
();
process
.
crash
.
crashed
=
false
;
process
.
env
=
env
::
vars
().
collect
();
then
!
(
dead
,
process
.
restarts
+=
1
);
then
!
(
dead
,
process
.
crash
.
value
+=
1
);
then
!
(
!
dead
,
process
.
crash
.
value
=
0
);
}
return
self
;
}
pub
fn
remove
(
&
mut
self
,
id
:
usize
)
{
if
let
Some
(
remote
)
=
&
self
.
remote
{
if
let
Err
(
err
)
=
http
::
remove
(
remote
,
id
)
{
crashln
!
(
"{} Failed to stop remove {id}
\n
Error: {:#?}"
,
*
helpers
::
FAIL
,
err
);
};
}
else
{
self
.
stop
(
id
);
self
.
list
.
remove
(
&
id
);
dump
::
write
(
&
self
);
}
}
pub
fn
set_id
(
&
mut
self
,
id
:
id
::
Id
)
{
self
.
id
=
id
;
self
.
id
.
next
();
dump
::
write
(
&
self
);
}
pub
fn
set_status
(
&
mut
self
,
id
:
usize
,
status
:
Status
)
{
self
.
process
(
id
).
running
=
status
.
to_bool
();
dump
::
write
(
&
self
);
}
pub
fn
items
(
&
self
)
->
BTreeMap
<
usize
,
Process
>
{
self
.
list
.
clone
()
}
pub
fn
items_mut
(
&
mut
self
)
->
&
mut
BTreeMap
<
usize
,
Process
>
{
&
mut
self
.
list
}
pub
fn
save
(
&
self
)
{
then
!
(
self
.
remote
.
is_none
(),
dump
::
write
(
&
self
))
}
pub
fn
count
(
&
mut
self
)
->
usize
{
self
.
list
().
count
()
}
pub
fn
is_empty
(
&
self
)
->
bool
{
self
.
list
.
is_empty
()
}
pub
fn
exists
(
&
self
,
id
:
usize
)
->
bool
{
self
.
list
.
contains_key
(
&
id
)
}
pub
fn
info
(
&
self
,
id
:
usize
)
->
Option
<&
Process
>
{
self
.
list
.
get
(
&
id
)
}
pub
fn
list
<'
l
>
(
&'
l
mut
self
)
->
impl
Iterator
<
Item
=
(
&'
l
usize
,
&'
l
mut
Process
)
>
{
self
.
list
.
iter_mut
().
map
(
|
(
k
,
v
)
|
(
k
,
v
))
}
pub
fn
process
(
&
mut
self
,
id
:
usize
)
->
&
mut
Process
{
self
.
list
.
get_mut
(
&
id
).
unwrap_or_else
(
||
crashln
!
(
"{} Process ({id}) not found"
,
*
helpers
::
FAIL
))
}
pub
fn
pid
(
&
self
,
id
:
usize
)
->
i64
{
self
.
list
.
get
(
&
id
).
unwrap_or_else
(
||
crashln
!
(
"{} Process ({id}) not found"
,
*
helpers
::
FAIL
)).
pid
}
pub
fn
find
(
&
self
,
name
:
&
str
)
->
Option
<
usize
>
{
self
.
list
.
iter
().
find
(
|
(
_
,
p
)
|
p
.
name
==
name
).
map
(
|
(
id
,
_
)
|
*
id
)
}
pub
fn
get
(
self
,
id
:
usize
)
->
ProcessWrapper
{
ProcessWrapper
{
id
,
runner
:
Arc
::
new
(
Mutex
::
new
(
self
)),
}
}
pub
fn
set_crashed
(
&
mut
self
,
id
:
usize
)
->
&
mut
Self
{
self
.
process
(
id
).
crash
.
crashed
=
true
;
return
self
;
}
pub
fn
set_children
(
&
mut
self
,
id
:
usize
,
children
:
Vec
<
i64
>
)
->
&
mut
Self
{
self
.
process
(
id
).
children
=
children
;
return
self
;
}
pub
fn
new_crash
(
&
mut
self
,
id
:
usize
)
->
&
mut
Self
{
self
.
process
(
id
).
crash
.
value
+=
1
;
return
self
;
}
pub
fn
stop
(
&
mut
self
,
id
:
usize
)
->
&
mut
Self
{
if
let
Some
(
remote
)
=
&
self
.
remote
{
if
let
Err
(
err
)
=
http
::
stop
(
remote
,
id
)
{
crashln
!
(
"{} Failed to stop process {id}
\n
Error: {:#?}"
,
*
helpers
::
FAIL
,
err
);
};
}
else
{
let
process
=
self
.
process
(
id
);
kill_children
(
process
.
children
.
clone
());
stop
(
process
.
pid
);
process
.
running
=
false
;
process
.
crash
.
crashed
=
false
;
process
.
crash
.
value
=
0
;
process
.
children
=
vec!
[];
}
return
self
;
}
pub
fn
rename
(
&
mut
self
,
id
:
usize
,
name
:
String
)
->
&
mut
Self
{
if
let
Some
(
remote
)
=
&
self
.
remote
{
if
let
Err
(
err
)
=
http
::
rename
(
remote
,
id
,
name
)
{
crashln
!
(
"{} Failed to rename process {id}
\n
Error: {:#?}"
,
*
helpers
::
FAIL
,
err
);
};
}
else
{
self
.
process
(
id
).
name
=
name
;
}
return
self
;
}
pub
fn
watch
(
&
mut
self
,
id
:
usize
,
path
:
&
str
,
enabled
:
bool
)
->
&
mut
Self
{
let
process
=
self
.
process
(
id
);
process
.
watch
=
Watch
{
enabled
,
path
:
string
!
(
path
),
hash
:
ternary
!
(
enabled
,
hash
::
create
(
process
.
path
.
join
(
path
)),
string
!
(
""
)),
};
return
self
;
}
pub
fn
fetch
(
&
self
)
->
Vec
<
ProcessItem
>
{
let
mut
processes
:
Vec
<
ProcessItem
>
=
Vec
::
new
();
for
(
id
,
item
)
in
self
.
items
()
{
let
mut
memory_usage
:
Option
<
MemoryInfo
>
=
None
;
let
mut
cpu_percent
:
Option
<
f32
>
=
None
;
if
let
Ok
(
mut
process
)
=
process
::
Process
::
new
(
item
.
pid
as
u32
)
{
let
mem_info_psutil
=
process
.
memory_info
().
ok
();
cpu_percent
=
process
.
cpu_percent
().
ok
();
memory_usage
=
Some
(
MemoryInfo
{
rss
:
mem_info_psutil
.
as_ref
().
unwrap
().
rss
(),
vms
:
mem_info_psutil
.
as_ref
().
unwrap
().
vms
(),
});
}
let
cpu_percent
=
match
cpu_percent
{
Some
(
percent
)
=>
format!
(
"{:.2}%"
,
percent
),
None
=>
string
!
(
"0.00%"
),
};
let
memory_usage
=
match
memory_usage
{
Some
(
usage
)
=>
helpers
::
format_memory
(
usage
.
rss
),
None
=>
string
!
(
"0b"
),
};
let
status
=
if
item
.
running
{
string
!
(
"online"
)
}
else
{
match
item
.
crash
.
crashed
{
true
=>
string
!
(
"crashed"
),
false
=>
string
!
(
"stopped"
),
}
};
processes
.
push
(
ProcessItem
{
id
,
status
,
pid
:
item
.
pid
,
cpu
:
cpu_percent
,
mem
:
memory_usage
,
restarts
:
item
.
restarts
,
name
:
item
.
name
.
clone
(),
start_time
:
item
.
started
,
watch_path
:
item
.
watch
.
path
.
clone
(),
uptime
:
helpers
::
format_duration
(
item
.
started
),
});
}
return
processes
;
}
}
impl
Process
{
/// Get a log paths of the process item
pub
fn
logs
(
&
self
)
->
LogInfo
{
let
name
=
self
.
name
.
replace
(
" "
,
"_"
);
LogInfo
{
out
:
global
!
(
"pmc.logs.out"
,
name
.
as_str
()),
error
:
global
!
(
"pmc.logs.error"
,
name
.
as_str
()),
}
}
}
impl
ProcessWrapper
{
/// Stop the process item
pub
fn
stop
(
&
mut
self
)
{
lock
!
(
self
.
runner
).
stop
(
self
.
id
).
save
();
}
/// Restart the process item
pub
fn
restart
(
&
mut
self
)
{
lock
!
(
self
.
runner
).
restart
(
self
.
id
,
false
).
save
();
}
/// Rename the process item
pub
fn
rename
(
&
mut
self
,
name
:
String
)
{
lock
!
(
self
.
runner
).
rename
(
self
.
id
,
name
).
save
();
}
/// Enable watching a path on the process item
pub
fn
watch
(
&
mut
self
,
path
:
&
str
)
{
lock
!
(
self
.
runner
).
watch
(
self
.
id
,
path
,
true
).
save
();
}
/// Disable watching on the process item
pub
fn
disable_watch
(
&
mut
self
)
{
lock
!
(
self
.
runner
).
watch
(
self
.
id
,
""
,
false
).
save
();
}
/// Set the process item as crashed
pub
fn
crashed
(
&
mut
self
)
{
lock
!
(
self
.
runner
).
restart
(
self
.
id
,
true
).
save
();
}
/// Get a json dump of the process item
pub
fn
fetch
(
&
self
)
->
ItemSingle
{
let
mut
runner
=
lock
!
(
self
.
runner
);
let
item
=
runner
.
process
(
self
.
id
);
let
config
=
config
::
read
().
runner
;
let
mut
memory_usage
:
Option
<
MemoryInfo
>
=
None
;
let
mut
cpu_percent
:
Option
<
f32
>
=
None
;
if
let
Ok
(
mut
process
)
=
process
::
Process
::
new
(
item
.
pid
as
u32
)
{
let
mem_info_psutil
=
process
.
memory_info
().
ok
();
cpu_percent
=
process
.
cpu_percent
().
ok
();
memory_usage
=
Some
(
MemoryInfo
{
rss
:
mem_info_psutil
.
as_ref
().
unwrap
().
rss
(),
vms
:
mem_info_psutil
.
as_ref
().
unwrap
().
vms
(),
});
}
let
status
=
if
item
.
running
{
string
!
(
"online"
)
}
else
{
match
item
.
crash
.
crashed
{
true
=>
string
!
(
"crashed"
),
false
=>
string
!
(
"stopped"
),
}
};
ItemSingle
{
info
:
Info
{
status
,
id
:
item
.
id
,
pid
:
item
.
pid
,
name
:
item
.
name
.
clone
(),
path
:
item
.
path
.
clone
(),
children
:
item
.
children
.
clone
(),
uptime
:
helpers
::
format_duration
(
item
.
started
),
command
:
format
!
(
"{} {} '{}'"
,
config
.
shell
,
config
.
args
.
join
(
" "
),
item
.
script
.
clone
()),
},
stats
:
Stats
{
cpu_percent
,
memory_usage
,
restarts
:
item
.
restarts
,
start_time
:
item
.
started
.
timestamp_millis
(),
},
watch
:
Watch
{
enabled
:
item
.
watch
.
enabled
,
hash
:
item
.
watch
.
hash
.
clone
(),
path
:
item
.
watch
.
path
.
clone
(),
},
log
:
Log
{
out
:
item
.
logs
().
out
,
error
:
item
.
logs
().
error
,
},
raw
:
Raw
{
running
:
item
.
running
,
crashed
:
item
.
crash
.
crashed
,
crashes
:
item
.
crash
.
value
,
},
}
}
}
pub
mod
dump
;
pub
mod
hash
;
pub
mod
http
;
pub
mod
id
;
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Dec 15, 6:36 PM (1 d, 13 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
484872
Default Alt Text
mod.rs (16 KB)
Attached To
Mode
rPMC Process Management Controller
Attached
Detach File
Event Timeline
Log In to Comment