Skip to content

Commit e84e9ac

Browse files
authored
Merge pull request #6 from qtc-de/dev
Prepare v1.4.0 Release
2 parents b0f034d + 3e0ac5a commit e84e9ac

File tree

11 files changed

+304
-277
lines changed

11 files changed

+304
-277
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

88

9+
## v1.4.0 - Mar 29, 2025
10+
11+
### Changed
12+
13+
* Switch from `vweb` to `veb` module
14+
* Switch from `x.json2` to `json` module
15+
* Get rid of global variables
16+
17+
918
## v1.3.0 - July 29, 2024
1019

1120
### Added

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ RES := resources/rpv-web.res
33
ICON := resources/rpv-web.ico
44
ICON86 := resources/rpv-web-x86.ico
55
ICON64 := resources/rpv-web-x64.ico
6-
Options := -os windows -prod -enable-globals -cflags $(shell pwd)/${RES}
6+
Options := -os windows -prod -cflags $(shell pwd)/${RES}
77
DebugOptions := ${Options} -d debug
88

99
rpv-web-x64 x64: ${RC}

frontend/src/components/Headline.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@
374374
<p id="Logo" class="d-inline-block">RP</p>
375375
<img id="LogoPng" src="../assets/icons/rpv-web.png"/>
376376
<p id="Web" class="d-inline-block">WEB</p>
377-
<p class="Fs-8 mb-0 ml-4">v1.3.0</p>
377+
<p class="Fs-8 mb-0 ml-4">v1.4.0</p>
378378
</div>
379379
380380
<div id="RightHeadline">

src/app.v

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
module main
2+
3+
import os
4+
import qtc_de.rpv
5+
6+
7+
// refresh obtains RPC information for currently running processes and stores
8+
// them within the internal context. If RPC information is already available, the
9+
// method attempts first to renew the already existing RPC info instead of requesting
10+
// new data.
11+
//
12+
// The method obtains a list of currently running process ids and iterates over them.
13+
// For each PID where an RpvProcessInformation is already present, it calls the update
14+
// method. For other processes, RpvProcessInformation is obtained via get_rpv_process_info.
15+
//
16+
// The method uses a global context and is currently not thread safe.
17+
fn (mut app App) refresh()!
18+
{
19+
self_pid := os.getpid()
20+
21+
arr_size := u32(0)
22+
mut pids := []u32{len: 1024}
23+
24+
if !C.EnumProcesses(pids.data, u32(pids.cap) * sizeof(u32), &arr_size)
25+
{
26+
return error('Failed to enumerate processes.')
27+
}
28+
29+
pids.trim(int(arr_size / sizeof(u32)))
30+
mut rpv_infos := []rpv.RpvProcessInformation{cap: int(arr_size / sizeof(u32)) - 1}
31+
32+
pid_loop:
33+
for pid in pids
34+
{
35+
if pid == self_pid
36+
{
37+
continue
38+
}
39+
40+
for mut web_info in app.processes
41+
{
42+
if web_info.pid == pid
43+
{
44+
web_info.rpv_info.update(mut app.symbol_resolver) or { break }
45+
rpv_infos << web_info.rpv_info
46+
continue pid_loop
47+
}
48+
}
49+
50+
if rpv_process_info := rpv.get_rpv_process_info(pid, mut app.symbol_resolver)
51+
{
52+
rpv_infos << rpv_process_info
53+
}
54+
}
55+
56+
app.processes.clear()
57+
58+
for info in rpv_infos
59+
{
60+
app.processes << convert_rpv_process_info(info, mut app.icon_cache, app.symbol_resolver)
61+
}
62+
63+
for child in app.processes
64+
{
65+
for mut parent in app.processes
66+
{
67+
if child.ppid == parent.pid
68+
{
69+
parent.childs << child
70+
}
71+
}
72+
}
73+
}
74+
75+
// get_process returns the RpvWebProcessInformation associated with
76+
// the specified pid. The pid is looked up in the global process array.
77+
pub fn (app &App) get_process(pid u32)? RpvWebProcessInformation
78+
{
79+
for ctr := 0; ctr < app.processes.len; ctr++
80+
{
81+
if app.processes[ctr].pid == pid
82+
{
83+
return app.processes[ctr]
84+
}
85+
}
86+
87+
return none
88+
}
89+
90+
// get_interface returns the RpvWebInterfaceInfo associated with the
91+
// specified pid und interface uuid. The pid is looked up within the
92+
// global interface array. The uuid is looked up within the rpc_info
93+
// of the corresponding process.
94+
pub fn (app &App) get_interface(pid u32, uuid string)? RpcWebInterfaceInfo
95+
{
96+
process := app.get_process(pid) or
97+
{
98+
return none
99+
}
100+
101+
for ctr := 0; ctr < process.rpc_info.interface_infos.len; ctr++
102+
{
103+
if process.rpc_info.interface_infos[ctr].id == uuid
104+
{
105+
return process.rpc_info.interface_infos[ctr]
106+
}
107+
}
108+
109+
return none
110+
}
111+
112+
// get_rpv_interface returns the rpv.RpcInterfaceInfo that is associated
113+
// with the specified pid and interface uuid. rpv.RpcInterfaceInfo contains
114+
// the full low level details of the RpcServer and can be used to e.g.
115+
// decompile the available functions. However, it should not be returned
116+
// within webserver responses, as it contains binary data.
117+
pub fn (app &App) get_rpv_interface(pid u32, uuid string)? rpv.RpcInterfaceInfo
118+
{
119+
process := app.get_process(pid) or
120+
{
121+
return none
122+
}
123+
124+
for ctr := 0; ctr < process.rpv_info.rpc_info.interface_infos.len; ctr++
125+
{
126+
if process.rpv_info.rpc_info.interface_infos[ctr].id == uuid
127+
{
128+
return process.rpv_info.rpc_info.interface_infos[ctr]
129+
}
130+
}
131+
132+
return none
133+
}
134+
135+
136+
// take_snapshot iterates over all objects within the global process array to
137+
// calculate their child relationship and to decompile their RPC interfaces.
138+
// All this information is stored in a WebSnapshot struct that is returned to
139+
// the caller.
140+
pub fn (app &App) take_snapshot() WebSnapshot
141+
{
142+
mut childs := []u32{cap: app.processes.len}
143+
144+
for process in app.processes
145+
{
146+
for child in process.childs
147+
{
148+
childs << child.pid
149+
}
150+
}
151+
152+
mut process_tree := []RpvWebProcessInformation{cap: app.processes.len - childs.len}
153+
mut idl_data := []WebIdlInterface{cap: app.processes.len}
154+
155+
for process in app.processes
156+
{
157+
if !childs.contains(process.pid)
158+
{
159+
process_tree << process
160+
}
161+
162+
for intf_info in process.rpv_info.rpc_info.interface_infos
163+
{
164+
midl_interface := intf_info.decode_all_methods(process.pid) or { continue }
165+
idl_data << convert_rpv_midl_interface(midl_interface)
166+
}
167+
}
168+
169+
return WebSnapshot {
170+
processes: process_tree
171+
idl_data: idl_data
172+
}
173+
}

src/main.v

Lines changed: 44 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,77 @@
11
module main
22

33
import os
4+
import veb
45
import json
5-
import vweb
66
import time
77
import qtc_de.rpv
88
import qtc_de.rpv.win
99
import cli { Command, Flag }
1010

11-
// In the current state of vweb, we have to use globals to track our
12-
// in memory state. Tracking this state within the App context does
13-
// currently not work correctly. Using a dedicated solution like an
14-
// sqlite database seems to be an overkill. Therefore, we use globals
15-
// as a temporary workaround.
16-
__global (
17-
g_icon_cache win.IconCache
18-
g_settings RpvWebSettings
19-
g_processes []RpvWebProcessInformation
20-
g_symbol_resolver rpv.SymbolResolver
21-
)
22-
23-
struct App {
24-
vweb.Context
11+
12+
pub struct Context
13+
{
14+
veb.Context
2515
}
2616

27-
struct RpvWebSettings {
17+
pub struct App
18+
{
19+
veb.StaticHandler
20+
pub mut:
21+
refresh_error string = 'Error while refreshing the process list.'
22+
icon_cache win.IconCache
23+
settings RpvWebSettings
24+
processes []RpvWebProcessInformation
25+
symbol_resolver rpv.SymbolResolver
26+
}
27+
28+
struct RpvWebSettings
29+
{
2830
symbol_file string
29-
mut:
31+
mut:
3032
symbol_path string
3133
}
3234

33-
pub fn (app App) before_request()
35+
pub fn (ctx Context) before_request()
3436
{
35-
println('[web] Incoming request: ${app.req.method} ${app.req.url}')
37+
println('[web] Incoming request: ${ctx.req.method} ${ctx.req.url}')
3638
}
3739

3840
fn main()
3941
{
4042
mut app := &App{}
4143

42-
app.serve_static('/favicon.ico', 'dist/favicon.ico')
43-
app.serve_static('/', 'dist/index.html')
44-
app.handle_static('dist', true)
44+
app.serve_static('/favicon.ico', 'dist/favicon.ico') or
45+
{
46+
println('[-] Unable to serve favicon.ico')
47+
return
48+
}
49+
50+
app.serve_static('/', 'dist/index.html') or
51+
{
52+
println('[-] Unable to serve index.html')
53+
return
54+
}
55+
56+
app.handle_static('dist', true) or
57+
{
58+
println('[-] Unable to serve dist folder')
59+
return
60+
}
4561

4662
mut cmd := cli.Command
4763
{
4864
name: 'rpv-web'
49-
description: 'An API interface to rpv'
50-
version: '1.3.0'
65+
description: 'An web API interface to rpv'
66+
version: '1.4.0'
5167
execute: fn [mut app] (cmd cli.Command)!
5268
{
5369
snapshot := cmd.flags.get_bool('snapshot') or { false }
5470
symbol_file := cmd.flags.get_string('symbol-file') or { 'rpv-web-symbols.toml' }
5571
pdb_path := cmd.flags.get_string('pdb-path') or { '' }
5672

57-
g_settings = RpvWebSettings { symbol_file: symbol_file, symbol_path: pdb_path }
58-
g_symbol_resolver = rpv.new_resolver(g_settings.symbol_file, g_settings.symbol_path) or
73+
app.settings = RpvWebSettings { symbol_file: symbol_file, symbol_path: pdb_path }
74+
app.symbol_resolver = rpv.new_resolver(app.settings.symbol_file, app.settings.symbol_path) or
5975
{
6076
eprintln('[-] Unable to initialize global symbol resolver: ${err}')
6177
return
@@ -72,7 +88,7 @@ fn main()
7288
}
7389

7490
println('[+] Taking a snapshot.')
75-
snap := take_snapshot()
91+
snap := app.take_snapshot()
7692

7793
filename := '${time.now().strftime('%Y.%m.%d-%H.%M')}-rpv-web-snapshot.json'
7894
println('[+] Writing result to ${filename}.')
@@ -86,7 +102,7 @@ fn main()
86102
else
87103
{
88104
port := cmd.flags.get_int('port') or { 8000 }
89-
vweb.run(app, port)
105+
veb.run[App, Context](mut app, port)
90106
}
91107
}
92108
}

0 commit comments

Comments
 (0)