yasysdui 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/bin/yasysdui +210 -0
- data/lib/yasysdui.rb +3 -0
- data/lib/yasysdui/command_button.rb +13 -0
- data/lib/yasysdui/index.rb +37 -0
- data/lib/yasysdui/service_catalog.rb +118 -0
- data/lib/yasysdui/service_controler.rb +55 -0
- data/lib/yasysdui/service_list.rb +128 -0
- data/lib/yasysdui/system_service.rb +147 -0
- metadata +66 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: dc6996d90288b984473de433a8fc90e844aa0befdf4dac1a3cfd0bce05f8b675
|
4
|
+
data.tar.gz: 00442c19eab41de2c19b92eaeb84862510d1663b0db49270f53020f0db07fd29
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0bedfb66c7e346bdaf8b0fe4e67809570a670005cbbc3d659d6d5d49ccc19bded39e6e250308fa53484d5ae67519555aab0919502194c078e7577dde6515ea75
|
7
|
+
data.tar.gz: 9ca1e5c4a163c7fe0b5d91340b2382b56a60617ec3a2fc12134abfd3844022adad8a757cfe249f88781159dd6f602b01363474ccf3eedd8927f2161763924f01
|
data/bin/yasysdui
ADDED
@@ -0,0 +1,210 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
=begin
|
3
|
+
Start/stop systemd service **on demand**
|
4
|
+
=end
|
5
|
+
|
6
|
+
|
7
|
+
require 'gtk3'
|
8
|
+
require 'yasysdui'
|
9
|
+
|
10
|
+
# Obsolete
|
11
|
+
#$MANAGED_SERVICES = Array.new
|
12
|
+
# Personalisation of managed services:
|
13
|
+
#
|
14
|
+
# syntax:
|
15
|
+
# $MANAGED_SERVICES << "service_name"
|
16
|
+
#$MANAGED_SERVICES << "vpnagentd"
|
17
|
+
#$MANAGED_SERVICES << "naclient"
|
18
|
+
|
19
|
+
#*********** List of services************************
|
20
|
+
def load_service_list
|
21
|
+
|
22
|
+
lsto = ServiceListStore.new_all_service
|
23
|
+
|
24
|
+
# lsto = ServiceListStore.new
|
25
|
+
# $MANAGED_SERVICES.each{|s| lsto.append_managed_service(s) }
|
26
|
+
|
27
|
+
lsto.sort_by_unit
|
28
|
+
|
29
|
+
$list=FilteredServiceListStore.new(lsto)
|
30
|
+
sci = ServiceCatalog.instance
|
31
|
+
|
32
|
+
# quick and dirty but working fast search (uses a full text index)
|
33
|
+
$list.set_visible_func( ){|model, iter|
|
34
|
+
|
35
|
+
if $list.filterstr
|
36
|
+
if $list.filterstr.length >= 2
|
37
|
+
servname = iter.get_value( Column_ID::Unit )
|
38
|
+
if servname
|
39
|
+
if rset = sci.idx[$list.filterstr]
|
40
|
+
rset.include?(servname)
|
41
|
+
end
|
42
|
+
else
|
43
|
+
false
|
44
|
+
end
|
45
|
+
else
|
46
|
+
true
|
47
|
+
end
|
48
|
+
else
|
49
|
+
true
|
50
|
+
end
|
51
|
+
}
|
52
|
+
end
|
53
|
+
### a search field
|
54
|
+
class FastSearchEntry < Gtk::SearchEntry
|
55
|
+
attr_accessor :tool_item
|
56
|
+
def initialize
|
57
|
+
super
|
58
|
+
@tool_item = Gtk::ToolItem.new
|
59
|
+
@tool_item.add(self)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
#*********************************************************
|
64
|
+
#The application toolbar, migh contain only global actions, like
|
65
|
+
# reload/exit/search ...
|
66
|
+
class AppToolbar < Gtk::Toolbar
|
67
|
+
def initialize
|
68
|
+
super
|
69
|
+
@item_count = 0
|
70
|
+
set_toolbar_style Gtk::ToolbarStyle::ICONS
|
71
|
+
@quit_bt = Gtk::ToolButton.new :stock_id => Gtk::Stock::QUIT
|
72
|
+
@reload_bt = Gtk::ToolButton.new :stock_id => Gtk::Stock::REFRESH
|
73
|
+
@fastsearch_in = FastSearchEntry.new
|
74
|
+
self.append(@reload_bt)
|
75
|
+
sep_start = Gtk::SeparatorToolItem.new
|
76
|
+
sep_start.draw = true; sep_start.expand = false
|
77
|
+
self.append(sep_start)
|
78
|
+
self.append(@fastsearch_in.tool_item)
|
79
|
+
sep_end = Gtk::SeparatorToolItem.new
|
80
|
+
sep_end.draw = false; sep_end.expand = true
|
81
|
+
self.append(sep_end)
|
82
|
+
self.append(@quit_bt)
|
83
|
+
end
|
84
|
+
def append(w)
|
85
|
+
self.insert(w, @item_count)
|
86
|
+
@item_count = @item_count + 1
|
87
|
+
end
|
88
|
+
def signal_quit_bt_clicked_connect(app)
|
89
|
+
@quit_bt.signal_connect("clicked"){ app.destroy; Gtk.main_quit }
|
90
|
+
end
|
91
|
+
def signal_reload_bt_clicked_connect(app)
|
92
|
+
@reload_bt.signal_connect("clicked"){ app.reload }
|
93
|
+
end
|
94
|
+
def signal_fastsearch_changed_connect(app)
|
95
|
+
@fastsearch_in.signal_connect("search_changed"){
|
96
|
+
app.do_fastsearch(@fastsearch_in.text)
|
97
|
+
}
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# the area to display informations about your units/services
|
102
|
+
# will have a scrolled list and a detail view parts
|
103
|
+
class InformationArea < Gtk::Paned
|
104
|
+
def initialize(scrolled_list, detail_area)
|
105
|
+
super(:vertical)
|
106
|
+
add1(scrolled_list)
|
107
|
+
add2(detail_area)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# The area to display a scrolled list of a part or all of your services
|
112
|
+
class ListArea < Gtk::ScrolledWindow
|
113
|
+
attr_accessor :trv
|
114
|
+
def initialize
|
115
|
+
super
|
116
|
+
load_service_list
|
117
|
+
@trv = ServiceListView.new($list)
|
118
|
+
add(@trv)
|
119
|
+
self.height_request=(300)
|
120
|
+
self.hscrollbar_policy = self.vscrollbar_policy = :automatic
|
121
|
+
# self.max_content_height = 400
|
122
|
+
# self.min_content_height = 300
|
123
|
+
end
|
124
|
+
end
|
125
|
+
# The area to display detailled information about a selected service
|
126
|
+
# and a service toolbar with some actions to be performed on
|
127
|
+
# the selected service
|
128
|
+
class DetailArea < Gtk::Box
|
129
|
+
def initialize(service_controler)
|
130
|
+
|
131
|
+
super(:vertical)
|
132
|
+
|
133
|
+
@tev = Gtk::TextView.new(service_controler.txtv)
|
134
|
+
@tev.editable = false
|
135
|
+
@tev.cursor_visible = false
|
136
|
+
|
137
|
+
|
138
|
+
@service_toolbar = ServiceToolbar.new(service_controler)
|
139
|
+
|
140
|
+
@tev_scroll = Gtk::ScrolledWindow.new
|
141
|
+
@tev_scroll.add(@tev)
|
142
|
+
@tev_scroll.height_request=(120)
|
143
|
+
@tev_scroll.hscrollbar_policy = @tev_scroll.vscrollbar_policy = :automatic
|
144
|
+
pack_start(@service_toolbar, :expand => false)
|
145
|
+
pack_end(@tev_scroll, :expand => true)
|
146
|
+
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
class ServiceToolbar < Gtk::ButtonBox
|
151
|
+
def initialize(service_controler)
|
152
|
+
super(:horizontal)
|
153
|
+
|
154
|
+
self.layout_style = :start
|
155
|
+
pack_start(service_controler.start_bt, :expand => false, :fill => false)
|
156
|
+
pack_start(service_controler.stop_bt, :expand =>false, :fill => false)
|
157
|
+
pack_start(service_controler.status_bt, :expand =>false, :fill => false)
|
158
|
+
pack_start(service_controler.restart_bt, :expand =>false, :fill => false)
|
159
|
+
|
160
|
+
end
|
161
|
+
end
|
162
|
+
# the thing that everybody would call main Window
|
163
|
+
|
164
|
+
# This will have:
|
165
|
+
# a global toolbar for global actions
|
166
|
+
# an information area (cf. below)
|
167
|
+
# a filter area (planned)
|
168
|
+
# no menubar yet
|
169
|
+
class Application < Gtk::Window
|
170
|
+
def initialize
|
171
|
+
super
|
172
|
+
signal_connect("delete_event") { false }
|
173
|
+
signal_connect("destroy") { puts "Bye Bye"; Gtk.main_quit }
|
174
|
+
self.border_width = 1
|
175
|
+
|
176
|
+
@list_area = ListArea.new
|
177
|
+
|
178
|
+
@txtv = Gtk::TextBuffer.new
|
179
|
+
|
180
|
+
@service_controler = ServiceControler.new(@txtv, @list_area.trv)
|
181
|
+
@detail_area = DetailArea.new(@service_controler)
|
182
|
+
@info_area = InformationArea.new(@list_area, @detail_area)
|
183
|
+
|
184
|
+
@vbox = Gtk::Box.new(:vertical)
|
185
|
+
@toolbar = AppToolbar.new
|
186
|
+
@toolbar.signal_quit_bt_clicked_connect(self)
|
187
|
+
@toolbar.signal_reload_bt_clicked_connect(self)
|
188
|
+
@toolbar.signal_fastsearch_changed_connect(self)
|
189
|
+
@vbox.pack_start(@toolbar, :expand => false)
|
190
|
+
@vbox.pack_end(@info_area, :expand => true)
|
191
|
+
|
192
|
+
add(@vbox)
|
193
|
+
self.set_default_size(800, 500)
|
194
|
+
end
|
195
|
+
def reload
|
196
|
+
$list.child_model.reload
|
197
|
+
@service_controler.connect_list_sel_to_textv
|
198
|
+
end
|
199
|
+
def do_fastsearch(text)
|
200
|
+
@list_area.trv.model.filterstr = text
|
201
|
+
@list_area.trv.model.refilter
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
app = Application.new
|
206
|
+
|
207
|
+
app.show_all
|
208
|
+
|
209
|
+
Gtk.main
|
210
|
+
|
data/lib/yasysdui.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'gtk3'
|
2
|
+
|
3
|
+
class CommandButton < Gtk::Button
|
4
|
+
|
5
|
+
attr_accessor :ctrl
|
6
|
+
attr_accessor :actionStr
|
7
|
+
def initialize(actionStr, ctrl)
|
8
|
+
super(:label => actionStr)
|
9
|
+
@ctrl = ctrl
|
10
|
+
@actionStr = actionStr
|
11
|
+
self.signal_connect("clicked") {@ctrl.execute(@actionStr)}
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
|
2
|
+
require 'set'
|
3
|
+
# quick and dirty "Full text" indexer for fastsearch.
|
4
|
+
# works fine for small amounts of data
|
5
|
+
|
6
|
+
module Indexable
|
7
|
+
|
8
|
+
def each_word
|
9
|
+
each_sentence{|sen|
|
10
|
+
sen.split(%r{[[:space:]]+|[[:punct:]]+}).each{|w| yield w }
|
11
|
+
}
|
12
|
+
end
|
13
|
+
|
14
|
+
def each_wordstart
|
15
|
+
each_word{|w|
|
16
|
+
wstart = ''
|
17
|
+
w.downcase.each_char{|c|
|
18
|
+
wstart << c
|
19
|
+
yield wstart
|
20
|
+
}
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
|
28
|
+
class SimpleIndex < Hash
|
29
|
+
def compile(obj)
|
30
|
+
obj.each_wordstart {|ws|
|
31
|
+
unless self.has_key?(ws)
|
32
|
+
self[ws] = Set.new
|
33
|
+
end
|
34
|
+
self[ws].add(obj)
|
35
|
+
}
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require_relative 'system_service'
|
3
|
+
|
4
|
+
|
5
|
+
class ServiceCatalog < Hash
|
6
|
+
include Singleton
|
7
|
+
attr_accessor :idx
|
8
|
+
def initialize
|
9
|
+
super
|
10
|
+
@idx = SimpleIndex.new
|
11
|
+
self.get_all_service
|
12
|
+
self.build_index
|
13
|
+
end
|
14
|
+
def rebuild
|
15
|
+
clear
|
16
|
+
self.get_all_service
|
17
|
+
self.build_index
|
18
|
+
end
|
19
|
+
def build_index
|
20
|
+
print "Building fast search index... "
|
21
|
+
self.each_value{|srv|
|
22
|
+
@idx.compile(srv)
|
23
|
+
}
|
24
|
+
print "done\n"
|
25
|
+
|
26
|
+
end
|
27
|
+
@@cmd_know_units = "sudo systemctl list-units -a -l -t service --no-pager --no-legend"
|
28
|
+
@@initd= "/etc/init.d"
|
29
|
+
@@initd_ignore = /README|.+~/
|
30
|
+
## UNIT LOAD ACTIVE SUB DESCRIPTION
|
31
|
+
@@rgx_ku = Regexp.new('\A([[:graph:]]+)\.service\s+' +
|
32
|
+
'([[:graph:]]+)\s+'+
|
33
|
+
'([[:alnum:]]+)\s+'+
|
34
|
+
'([[:alnum:]]+)\s+'+
|
35
|
+
'([[:print:]]+)\Z')
|
36
|
+
@@cmd_list_unit_files = "sudo systemctl list-unit-files -a -l -t service --no-pager --no-legend"
|
37
|
+
## UNIT STATE
|
38
|
+
@@rgx_li = Regexp.new('\A([[:graph:]]+)\.service\s+' +
|
39
|
+
'([[:graph:]]+)\Z')
|
40
|
+
|
41
|
+
# get all know systemd native units from systemctl list-units
|
42
|
+
def get_all_known_units
|
43
|
+
|
44
|
+
match = nil; srv = nil
|
45
|
+
stdout_str, status = Open3.capture2( @@cmd_know_units )
|
46
|
+
if status.success?
|
47
|
+
stdout_str.each_line{|l|
|
48
|
+
match = @@rgx_ku.match(l.strip)
|
49
|
+
if match
|
50
|
+
srv = SystemService.new(match[1] )
|
51
|
+
srv.load = match[2]
|
52
|
+
srv.active = match[3]
|
53
|
+
srv.sub = match[4]
|
54
|
+
srv.descr = match[5]
|
55
|
+
self[srv] = srv
|
56
|
+
else
|
57
|
+
puts l
|
58
|
+
raise "Failed parsing line printed above"
|
59
|
+
end
|
60
|
+
}
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
64
|
+
# add all know systemd units files from systemctl list-unit-files
|
65
|
+
def add_units_from_file_list
|
66
|
+
|
67
|
+
match = nil; srv = nil
|
68
|
+
stdout_str, status = Open3.capture2( @@cmd_list_unit_files )
|
69
|
+
if status.success?
|
70
|
+
stdout_str.each_line{|l|
|
71
|
+
match = @@rgx_li.match(l.strip)
|
72
|
+
if match
|
73
|
+
f = match[1]
|
74
|
+
unless f.end_with?('@') # ignore template units for now...
|
75
|
+
unless self.has_key?(f)
|
76
|
+
srv = SystemService.new( f )
|
77
|
+
srv.state = match[2]
|
78
|
+
srv.read_status_info
|
79
|
+
self[srv] = srv if srv
|
80
|
+
else
|
81
|
+
self[f].state = match[2]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
else
|
85
|
+
puts l
|
86
|
+
raise "Failed parsing line printed above"
|
87
|
+
end
|
88
|
+
}
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|
92
|
+
# get all legacy init.d scripts
|
93
|
+
def add_init_d_scripts
|
94
|
+
flist = Dir.glob("#@@initd/*").reject{|f| @@initd_ignore.match(f) }.map{|f| File.basename(f)}
|
95
|
+
flist.each{|f|
|
96
|
+
# only try to add an init script if it does not exist as an native systemd unit,
|
97
|
+
# as these have precedence
|
98
|
+
# these assumes we have to read native service before
|
99
|
+
if self.has_key?(f)
|
100
|
+
srv = self[f]
|
101
|
+
srv.overrides_legacy = true
|
102
|
+
srv.read_state
|
103
|
+
else
|
104
|
+
srv = SystemServiceLegacy.new( f )
|
105
|
+
self[srv] = srv if srv
|
106
|
+
end
|
107
|
+
}
|
108
|
+
end
|
109
|
+
# get all available units/scripts
|
110
|
+
def get_all_service
|
111
|
+
pp :getting_all_services
|
112
|
+
get_all_known_units; pp :got_all_known_units
|
113
|
+
add_units_from_file_list; pp :got_all_units_from_file
|
114
|
+
add_init_d_scripts; pp :got_all_init_scripts
|
115
|
+
puts "Built service catalog with #{self.size} item(s)"
|
116
|
+
self
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
|
2
|
+
require 'open3'
|
3
|
+
require_relative 'command_button'
|
4
|
+
require_relative 'service_list'
|
5
|
+
require_relative 'system_service'
|
6
|
+
|
7
|
+
class ServiceControler
|
8
|
+
attr_accessor :service
|
9
|
+
attr_accessor :start_bt
|
10
|
+
attr_accessor :stop_bt
|
11
|
+
attr_accessor :status_bt
|
12
|
+
attr_accessor :restart_bt
|
13
|
+
attr_accessor :txtv
|
14
|
+
attr_accessor :listv
|
15
|
+
@@cmd = "sudo systemctl"
|
16
|
+
|
17
|
+
def initialize(txtv, listv)
|
18
|
+
@txtv = txtv
|
19
|
+
self.init_textv
|
20
|
+
@listv = listv
|
21
|
+
@start_bt = CommandButton.new("Start", self)
|
22
|
+
@stop_bt = CommandButton.new("Stop",self)
|
23
|
+
@status_bt = CommandButton.new("Status", self)
|
24
|
+
@restart_bt = CommandButton.new("Restart", self)
|
25
|
+
connect_list_sel_to_textv
|
26
|
+
end
|
27
|
+
def init_textv
|
28
|
+
@txtv.text = "No service selected"
|
29
|
+
end
|
30
|
+
def connect_list_sel_to_textv
|
31
|
+
@listv.signal_connect_sel_changed(self)
|
32
|
+
end
|
33
|
+
def execute(action)
|
34
|
+
if @service then
|
35
|
+
@action_output, @action_call_status = Open3.capture2e("#@@cmd #{action.downcase} #@service" )
|
36
|
+
self.ui_update(action)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
def ui_update(action)
|
40
|
+
# (always) update textView buffer text
|
41
|
+
result_text = "#{action}: #{@service.descr} "
|
42
|
+
result_text << "\n Result: #{@action_call_status}"
|
43
|
+
result_text << "\n" + @action_output
|
44
|
+
@txtv.text = result_text
|
45
|
+
# update status info in the result list
|
46
|
+
if action.downcase == 'status'
|
47
|
+
@status_output = @action_output
|
48
|
+
else # not sure if this makes senses in case systemctl call would be non-blocking
|
49
|
+
# but otherwise it makes
|
50
|
+
@status_output, status_call_status = Open3.capture2e("#@@cmd status #@service" )
|
51
|
+
end
|
52
|
+
@service.set_status_info_from_output( @status_output )
|
53
|
+
@listv.update_selected_row
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
require_relative 'service_catalog'
|
4
|
+
|
5
|
+
module Column_ID
|
6
|
+
Unit = 0
|
7
|
+
Load = 1
|
8
|
+
Active = 2
|
9
|
+
Sub = 3
|
10
|
+
Descr = 4
|
11
|
+
Native_info = 5
|
12
|
+
State = 6
|
13
|
+
end
|
14
|
+
|
15
|
+
class FilteredServiceListStore < Gtk::TreeModelFilter
|
16
|
+
attr_accessor :filterstr
|
17
|
+
end
|
18
|
+
|
19
|
+
class ServiceListStore < Gtk::ListStore
|
20
|
+
include Column_ID
|
21
|
+
def self.new_all_service
|
22
|
+
sl = ServiceListStore.new
|
23
|
+
sl.load_catalog
|
24
|
+
sl
|
25
|
+
end
|
26
|
+
def initialize
|
27
|
+
super(String, String, String, String, String, String, String)
|
28
|
+
end
|
29
|
+
def load_catalog
|
30
|
+
sci = ServiceCatalog.instance
|
31
|
+
puts "Loading Catalog with #{sci.size} item(s)"
|
32
|
+
sci.each_value{|s| append_service(s) }
|
33
|
+
end
|
34
|
+
def reload
|
35
|
+
clear # clear Gtk::ListStore
|
36
|
+
ServiceCatalog.instance.rebuild
|
37
|
+
load_catalog
|
38
|
+
end
|
39
|
+
def append_managed_service(name)
|
40
|
+
if service = ServiceCatalog.instance[name]
|
41
|
+
append_service(service)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
def append_service(service)
|
45
|
+
row = self.append
|
46
|
+
row[ Unit ] = service
|
47
|
+
row[ Load ] = service.load
|
48
|
+
row[ Active ] = service.active
|
49
|
+
row[ Sub ] = service.sub
|
50
|
+
row[ Descr ] = service.descr
|
51
|
+
row[ Native_info ] = service.native_info
|
52
|
+
row[ State ] = service.state
|
53
|
+
end
|
54
|
+
def sort_by_unit
|
55
|
+
self.set_sort_column_id( Unit, :ascending)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class TreeViewColumnToggle < Gtk::TreeViewColumn
|
60
|
+
@@cell_renderer = Gtk::CellRendererToggle.new
|
61
|
+
def initialize(title, column_id )
|
62
|
+
attrs = { :active => column_id }
|
63
|
+
super(title, @@cell_renderer , attrs )
|
64
|
+
self.resizable = true
|
65
|
+
self.sort_column_id = column_id
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class TreeViewColumnText < Gtk::TreeViewColumn
|
70
|
+
@@cell_renderer = Gtk::CellRendererText.new
|
71
|
+
def initialize(title, column_id )
|
72
|
+
attrs = { :text => column_id }
|
73
|
+
super(title, @@cell_renderer , attrs )
|
74
|
+
self.resizable = true
|
75
|
+
self.sort_column_id = column_id
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class ServiceListView < Gtk::TreeView
|
80
|
+
include Column_ID
|
81
|
+
def initialize(list)
|
82
|
+
super(list)
|
83
|
+
@list = list
|
84
|
+
@col1 = TreeViewColumnText.new("Unit", Unit )
|
85
|
+
|
86
|
+
@col1.fixed_width = 200
|
87
|
+
@col1.sort_indicator = true
|
88
|
+
|
89
|
+
@col2 = TreeViewColumnText.new("Load", Load )
|
90
|
+
@col3 = TreeViewColumnText.new("Active", Active )
|
91
|
+
@col4 = TreeViewColumnText.new("Sub", Sub)
|
92
|
+
@col5 = TreeViewColumnText.new("Description", Descr)
|
93
|
+
@col6 = TreeViewColumnText.new("Native?", Native_info)
|
94
|
+
@col7 = TreeViewColumnText.new("State", State)
|
95
|
+
|
96
|
+
|
97
|
+
self.append_column(@col1)
|
98
|
+
self.append_column(@col6)
|
99
|
+
self.append_column(@col7)
|
100
|
+
self.append_column(@col2)
|
101
|
+
self.append_column(@col3)
|
102
|
+
self.append_column(@col4)
|
103
|
+
self.append_column(@col5)
|
104
|
+
|
105
|
+
self.enable_search = true
|
106
|
+
end
|
107
|
+
|
108
|
+
def signal_connect_sel_changed(sctrl)
|
109
|
+
@sel = self.selection
|
110
|
+
@sel.mode = :browse
|
111
|
+
@sel.signal_connect("changed"){
|
112
|
+
if @sel.selected
|
113
|
+
@service = ServiceCatalog.instance[@sel.selected[Unit] ]
|
114
|
+
sctrl.service = @service
|
115
|
+
sctrl.execute("Status")
|
116
|
+
else
|
117
|
+
sctrl.init_textv
|
118
|
+
end
|
119
|
+
}
|
120
|
+
end
|
121
|
+
|
122
|
+
def update_selected_row
|
123
|
+
iter = self.model.convert_iter_to_child_iter( @sel.selected )
|
124
|
+
iter.set_value(Load, @service.load)
|
125
|
+
iter.set_value(Active, @service.active)
|
126
|
+
iter.set_value(Sub, @service.sub)
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
require_relative 'index'
|
2
|
+
require 'pp'
|
3
|
+
|
4
|
+
class SystemService < String
|
5
|
+
include Indexable
|
6
|
+
attr_accessor :descr
|
7
|
+
attr_accessor :state
|
8
|
+
attr_accessor :load
|
9
|
+
attr_accessor :active
|
10
|
+
attr_accessor :sub
|
11
|
+
attr_accessor :overrides_legacy
|
12
|
+
attr_accessor :filename_full
|
13
|
+
|
14
|
+
@@cmd_info = "sudo systemctl status"
|
15
|
+
@@descr_rgx = /\A.+\.service\s+-\s+([[:print:]]+)\Z/
|
16
|
+
@@descr_masked_rgx = /\A.+\.service\Z/
|
17
|
+
@@loaded_rgx = /\ALoaded:\s+([[:graph:]]+)\s+\((.*)\)\Z/
|
18
|
+
@@active_rgx = /\AActive:\s+([[:graph:]]+)\s+\(([[:graph:]]+)\).*\Z/
|
19
|
+
@@warning_rgx = /\AWarning:/
|
20
|
+
@@status_read_failed_rgx = Regexp.new('\AFailed to get properties:.*\Z')
|
21
|
+
@@status_unit_notfound_rgx = Regexp.new('\AUnit\s[[:graph:]]+\scould\snot\sbe\sfound.\Z')
|
22
|
+
def set_status_info_from_output( action_output )
|
23
|
+
@status_info_raw = action_output
|
24
|
+
self.parse_status_info
|
25
|
+
end
|
26
|
+
# This provides the texts to index (module Indexable)
|
27
|
+
def each_sentence
|
28
|
+
yield self.to_str
|
29
|
+
yield @descr unless @descr.nil?
|
30
|
+
end
|
31
|
+
def parse_status_info
|
32
|
+
match = nil
|
33
|
+
return if self.handle_masked_info
|
34
|
+
info_tab = @status_info_raw.lines.reject{|l| @@warning_rgx.match(l) }
|
35
|
+
info_tab.map!{|r| r.strip}
|
36
|
+
if @@status_read_failed_rgx.match(info_tab[0])
|
37
|
+
return nil
|
38
|
+
end
|
39
|
+
if @@status_unit_notfound_rgx.match(info_tab[0])
|
40
|
+
return nil
|
41
|
+
end
|
42
|
+
if match = @@descr_rgx.match(info_tab[0])
|
43
|
+
self.descr = match[1]
|
44
|
+
else
|
45
|
+
# masked script/service without description returned
|
46
|
+
if match = @@descr_masked_rgx.match(info_tab[0])
|
47
|
+
else
|
48
|
+
puts info_tab[0]
|
49
|
+
raise "Failed parsing line printed above"
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
# complete data
|
54
|
+
if match
|
55
|
+
match = @@loaded_rgx.match(info_tab[1])
|
56
|
+
if match
|
57
|
+
self.load = match[1]
|
58
|
+
load_info = match[2].split(';')
|
59
|
+
self.filename_full = load_info[0]
|
60
|
+
self.state = load_info[1]
|
61
|
+
end
|
62
|
+
match = @@active_rgx.match(info_tab[2].strip)
|
63
|
+
if match
|
64
|
+
self.active = match[1]
|
65
|
+
self.sub = match[2]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
def handle_masked_info
|
71
|
+
if self.state == 'masked'
|
72
|
+
@load = 'masked'
|
73
|
+
@active = 'inactive'
|
74
|
+
@sub = 'dead'
|
75
|
+
true
|
76
|
+
else
|
77
|
+
false
|
78
|
+
end
|
79
|
+
end
|
80
|
+
def read_status_info
|
81
|
+
return nil if self.end_with?("@")
|
82
|
+
return if self.handle_masked_info
|
83
|
+
@status_info_raw, status = Open3.capture2("#@@cmd_info #{self.to_s}" )
|
84
|
+
if @status_info_raw.lines.size > 0
|
85
|
+
self.parse_status_info
|
86
|
+
else
|
87
|
+
# TODO: a more decent error handling...
|
88
|
+
puts "Failed getting service status info for"
|
89
|
+
pp self
|
90
|
+
end
|
91
|
+
end
|
92
|
+
def read_state
|
93
|
+
return nil if self.end_with?("@")
|
94
|
+
match = nil
|
95
|
+
stdout_str, status = Open3.capture2("#@@cmd_info #{self.to_s}" )
|
96
|
+
if stdout_str.lines.size > 0
|
97
|
+
|
98
|
+
info_tab = stdout_str.lines.reject{|l| @@warning_rgx.match(l) }
|
99
|
+
if @@status_read_failed_rgx.match(info_tab[0].strip)
|
100
|
+
return nil
|
101
|
+
end
|
102
|
+
match = @@loaded_rgx.match(info_tab[1].strip)
|
103
|
+
if match
|
104
|
+
load_info = match[2].split(';')
|
105
|
+
self.filename_full = load_info[0]
|
106
|
+
self.state = load_info[1]
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
else
|
111
|
+
puts "Failed getting service state info for"
|
112
|
+
pp self
|
113
|
+
|
114
|
+
end
|
115
|
+
end
|
116
|
+
# default constructor, takes unit name
|
117
|
+
def initialize(str)
|
118
|
+
super(str)
|
119
|
+
@overrides_legacy = false
|
120
|
+
end
|
121
|
+
def native_info
|
122
|
+
# yes+ : native and overrides init script
|
123
|
+
# yes : native and no init script
|
124
|
+
# no : init script
|
125
|
+
if self.native?
|
126
|
+
@overrides_legacy ? "yes+" : "yes"
|
127
|
+
else
|
128
|
+
"no"
|
129
|
+
end
|
130
|
+
end
|
131
|
+
def native?
|
132
|
+
true
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
|
137
|
+
class SystemServiceLegacy < SystemService
|
138
|
+
|
139
|
+
def initialize(str)
|
140
|
+
super(str)
|
141
|
+
self.read_status_info
|
142
|
+
end
|
143
|
+
def native?
|
144
|
+
false
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
metadata
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: yasysdui
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Denis Mertz
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-09-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: gtk3
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.2'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.2'
|
27
|
+
description: A quick and dirty gtk3 ui wrapper around systemd/systemctl
|
28
|
+
email: dev@familie-mertz.eu
|
29
|
+
executables:
|
30
|
+
- yasysdui
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- bin/yasysdui
|
35
|
+
- lib/yasysdui.rb
|
36
|
+
- lib/yasysdui/command_button.rb
|
37
|
+
- lib/yasysdui/index.rb
|
38
|
+
- lib/yasysdui/service_catalog.rb
|
39
|
+
- lib/yasysdui/service_controler.rb
|
40
|
+
- lib/yasysdui/service_list.rb
|
41
|
+
- lib/yasysdui/system_service.rb
|
42
|
+
homepage: https://gitlab.com/d1mertz1/yasysdui
|
43
|
+
licenses:
|
44
|
+
- MIT
|
45
|
+
metadata: {}
|
46
|
+
post_install_message:
|
47
|
+
rdoc_options: []
|
48
|
+
require_paths:
|
49
|
+
- lib
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '0'
|
60
|
+
requirements: []
|
61
|
+
rubyforge_project:
|
62
|
+
rubygems_version: 2.7.6
|
63
|
+
signing_key:
|
64
|
+
specification_version: 4
|
65
|
+
summary: Yet another systemd ui
|
66
|
+
test_files: []
|