├── 10-service_status.rules
├── README.md
├── img
└── services-systemd.png
├── org.freedesktop.policykit.pkexec.systemctl.policy
└── services-systemd@abteil.org
├── extension.js
├── metadata.json
├── popupServiceItem.js
├── prefs.js
├── schemas
├── gschemas.compiled
└── org.gnome.shell.extensions.services-systemd.gschema.xml
└── stylesheet.css
/10-service_status.rules:
--------------------------------------------------------------------------------
1 | polkit.addRule(function(action, subject) {
2 | if (action.id == "org.freedesktop.systemd1.manage-units" && subject.isInGroup("wheel") ) {
3 | var verb = action.lookup("verb");
4 | if (verb == "start" || verb == "stop" || verb == "restart" || verb == "enable" || verb == "disable") {
5 | return polkit.Result.YES;
6 | }
7 | }
8 | });
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Services Systemd
2 | ## About
3 | Services Systemd is a [Gnome](https://www.gnome.org/) Shell Extension which
4 | allows to start and stop systemd services via a menu in the status area in the
5 | main menu panel. As there exists all a lot of irrelevant systemd services - in
6 | the sense of being displayed in this menu - the user can preselect which
7 | services should be shown in the preference dialog of the extension.
8 |
9 | 
10 |
11 | ## Install
12 |
13 | ### Gnome Shell Extensions Page
14 | The easiest way to install this extension is via the official [Gnome Shell Extensions](https://extensions.gnome.org) resource page: https://extensions.gnome.org/extension/1034/services-systemd/
15 |
16 | ### Arch Linux
17 | For Arch Linux the AUR package [gnome-shell-extension-services-systemd-git](https://aur4.archlinux.org/packages/gnome-shell-extension-services-systemd-git/) is provided.
18 |
19 | ## Authorization
20 | Done via a password prompt from the command `pkexec` of the polkit package.
21 | This command usually pops up a graphical password prompt.
22 |
23 | ### Without Password Prompt
24 |
25 | #### Using pkexec (default)
26 | In the recent versions of this extension the authorization is done by `pkexec`
27 | (before via `gksu`). Therefore if you would like to be able to start systemd
28 | services without getting prompted for a password, you will have to configure a
29 | polkit policy. The policy file [org.freedesktop.policykit.pkexec.systemctl.policy](org.freedesktop.policykit.pkexec.systemctl.policy)
30 | would allow the execution of `systemctl [start|stop]` without a password
31 | confirmation. Simple copy the file in your polkit policy folder (usually:
32 | `/usr/share/polkit-1/actions`).
33 |
34 | #### Using systemctl
35 | You can also choose to use `systemctl` natively and bypass a password prompt.
36 |
37 | To do this, add the policy file
38 | [10-service_status.rules](10-service_status.rules) to `/etc/polkit-1/rules.d`.
39 |
40 | Feel free to change the `wheel` group noted in the file to any other group that
41 | you see fit.
42 |
43 | ## Future
44 | **Planned additional functionality:**
45 | * Separators/Groups
46 | * Adjustable systemd folders
47 | * Other services
48 |
49 | ## Credits
50 | Some parts have been taken from the gnome extension [Services](https://github.com/hjr265/gnome-shell-extension-services).
51 |
52 | ## License
53 | [GPLv3](http://www.gnu.org/licenses/gpl-3.0.en.html)
54 |
--------------------------------------------------------------------------------
/img/services-systemd.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/petres/gnome-shell-extension-services-systemd/86936eff0f6f87d1f8f3b0270472881234064aa2/img/services-systemd.png
--------------------------------------------------------------------------------
/org.freedesktop.policykit.pkexec.systemctl.policy:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 | Start/Start/Restart Services Systemd
6 | http://www.freedesktop.org/wiki/Software/polkit/
7 |
8 | Start systemd service
9 | Authentication is required to start a systemd service
10 | system-run-symbolic
11 |
12 | no
13 | no
14 | yes
15 |
16 | /usr/bin/systemctl
17 | start
18 |
19 |
20 | Stop systemd service
21 | Authentication is required to stop a systemd service
22 | system-run-symbolic
23 |
24 | no
25 | no
26 | yes
27 |
28 | /usr/bin/systemctl
29 | stop
30 |
31 |
32 | Restart systemd service
33 | Authentication is required to restart a systemd service
34 | system-run-symbolic
35 |
36 | no
37 | no
38 | yes
39 |
40 | /usr/bin/systemctl
41 | restart
42 |
43 |
44 |
--------------------------------------------------------------------------------
/services-systemd@abteil.org/extension.js:
--------------------------------------------------------------------------------
1 | const GLib = imports.gi.GLib;
2 | const Lang = imports.lang;
3 | const Main = imports.ui.main;
4 | const PanelMenu = imports.ui.panelMenu;
5 | const PopupMenu = imports.ui.popupMenu;
6 | const St = imports.gi.St;
7 | const ExtensionUtils = imports.misc.extensionUtils;
8 |
9 | const Util = imports.misc.util;
10 |
11 | const Me = ExtensionUtils.getCurrentExtension();
12 | //const ScrollablePopupMenu = Me.imports.scrollablePopupMenu.ScrollablePopupMenu;
13 | var PopupServiceItem = Me.imports.popupServiceItem.PopupServiceItem;
14 |
15 | const ServicesManager = new Lang.Class({
16 | Name: 'ServicesManager',
17 | _entries: [],
18 | _containerType: -1,
19 |
20 | _init: function() {
21 | this._settings = ExtensionUtils.getSettings();
22 | this._settings.connect('changed', Lang.bind(this, this._loadConfig));
23 |
24 | this._createContainer();
25 | this._loadConfig();
26 | this._refresh();
27 | },
28 | _createContainer: function() {
29 | this._containerType = this._settings.get_enum('position');
30 |
31 | if (this._containerType == 0) {
32 | this.container = new PanelMenu.Button(0.0);
33 |
34 | let hbox = new St.BoxLayout({ style_class: 'panel-status-menu-box' });
35 | let icon = new St.Icon({icon_name: 'system-run-symbolic', style_class: 'system-status-icon'});
36 | hbox.add_child(icon);
37 |
38 | this.container.add_actor(hbox);
39 | this.container.add_style_class_name('panel-status-button');
40 |
41 | this.container.connect('button-press-event', Lang.bind(this, function() {
42 | this._refresh();
43 | }));
44 | Main.panel.addToStatusArea('servicesManager', this.container);
45 | } else {
46 | this.container = new PopupMenu.PopupSubMenuMenuItem("Systemd Services", true);
47 | //this.container.icon.style_class = 'system-extensions-submenu-icon';
48 | this.container.icon.icon_name = 'system-run-symbolic';
49 |
50 | Main.panel.statusArea.aggregateMenu.menu.addMenuItem(this.container, 8);
51 | }
52 |
53 | this.container.connect('button-press-event', Lang.bind(this, function() {
54 | this._refresh();
55 | }));
56 | },
57 | _getCommand: function(service, action, type) {
58 | let command = `systemctl ${action} ${service} --${type}`
59 | if (type == "system" && action != 'is-active') {
60 | if (this._settings.get_enum("command-method") == 0) {
61 | command = `pkexec --user root ${command}`
62 | } else if (this._settings.get_enum("command-method") == 2) {
63 | command = `sudo ${command}`
64 | }
65 | }
66 | return `sh -c "${command}; exit;"`
67 | },
68 | _refresh: function() {
69 | this.container.menu.removeAll();
70 |
71 | let restartButton = this._settings.get_boolean('show-restart')
72 |
73 | this._entries.forEach(Lang.bind(this, function(service) {
74 | let active = false;
75 | let [_, out, err, stat] = GLib.spawn_command_line_sync(
76 | this._getCommand(service['service'], 'is-active', service["type"]));
77 | active = (stat == 0);
78 |
79 | let serviceItem = new PopupServiceItem(service['name'], active, {'restartButton': restartButton});
80 | this.container.menu.addMenuItem(serviceItem);
81 |
82 | serviceItem.connect('toggled', Lang.bind(this, function() {
83 | GLib.spawn_command_line_async(
84 | this._getCommand(service['service'], (active ? 'stop' : 'start'), service["type"]));
85 | }));
86 |
87 | if (serviceItem.restartButton)
88 | serviceItem.restartButton.connect('clicked', Lang.bind(this, function() {
89 | GLib.spawn_command_line_async(
90 | this._getCommand(service['service'], 'restart', service["type"]));
91 | this.container.menu.close();
92 | }));
93 | }));
94 | if(this._containerType == 0 && this._settings.get_boolean('show-add')) {
95 | if(this._entries.length > 0)
96 | this.container.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
97 |
98 | let item = new PopupMenu.PopupMenuItem(_("Add systemd services ..."));
99 | item.connect('activate', Lang.bind(this, function() {
100 | Util.spawn(["gnome-shell-extension-prefs", "services-systemd@abteil.org"]);
101 | this.container.menu.close();
102 | }));
103 | this.container.menu.addMenuItem(item);
104 | }
105 | return true;
106 | },
107 | _loadConfig: function() {
108 | if (this._containerType != this._settings.get_enum('position')) {
109 | this.container.destroy();
110 | this._createContainer();
111 | }
112 |
113 | let entries = this._settings.get_strv("systemd");
114 | this._entries = []
115 | for (let i = 0; i < entries.length; i++) {
116 | let entry = JSON.parse(entries[i]);
117 | if (!("type" in entry))
118 | entry["type"] = "system"
119 | this._entries.push(entry);
120 | }
121 | },
122 | destroy: function() {
123 | this.container.destroy();
124 | }
125 | });
126 |
127 | let serviceManager;
128 |
129 | function enable() {
130 | serviceManager = new ServicesManager();
131 | }
132 |
133 | function disable() {
134 | serviceManager.destroy();
135 | }
136 |
--------------------------------------------------------------------------------
/services-systemd@abteil.org/metadata.json:
--------------------------------------------------------------------------------
1 | {
2 | "uuid": "services-systemd@abteil.org",
3 | "name": "Services Systemd",
4 | "url": "https://github.com/petres/gnome-shell-extension-services-systemd",
5 | "description": "Toggle systemd services on/off from a popup menu in the top gnome panel. Can be used to start services like apache2, mysql, postgres. It uses `pkexec' to run `sytemctl'. If you want to start services without entering a password you have to polkit policy file. An example policy file can be found in the github repository.",
6 | "settings-schema": "org.gnome.shell.extensions.services-systemd",
7 | "shell-version": [
8 | "3.34"
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/services-systemd@abteil.org/popupServiceItem.js:
--------------------------------------------------------------------------------
1 | const Lang = imports.lang;
2 | const PopupMenu = imports.ui.popupMenu;
3 | const Util = imports.misc.util;
4 | const { GObject, Gtk, St, Clutter} = imports.gi;
5 |
6 | const ExtensionSystem = imports.ui.extensionSystem;
7 | const ExtensionUtils = imports.misc.extensionUtils;
8 |
9 | var PopupServiceItem = GObject.registerClass(
10 | class PopupServiceItem extends PopupMenu.PopupSwitchMenuItem {
11 | _init(text, active, params) {
12 | super._init(text, active);
13 |
14 | if (params.restartButton) {
15 | this.restartButton = new St.Button({
16 | x_align: St.Align.END,
17 | x_expand: false,
18 | reactive: true,
19 | can_focus: true,
20 | track_hover: true,
21 | accessible_name: 'restart',
22 | style_class: 'system-menu-action services-systemd-button-reload'
23 | });
24 |
25 | this.restartButton.child = new St.Icon({ icon_name: 'view-refresh-symbolic' });
26 | this.add_child(this.restartButton);
27 | }
28 | }
29 | });
30 |
--------------------------------------------------------------------------------
/services-systemd@abteil.org/prefs.js:
--------------------------------------------------------------------------------
1 | const GLib = imports.gi.GLib;
2 | const Gio = imports.gi.Gio;
3 | const Gtk = imports.gi.Gtk;
4 | const GObject = imports.gi.GObject;
5 | const Lang = imports.lang;
6 | const ByteArray = imports.byteArray.ByteArray;
7 |
8 | const ExtensionUtils = imports.misc.extensionUtils;
9 | const Me = ExtensionUtils.getCurrentExtension();
10 |
11 |
12 | const ServicesSystemdSettings = new GObject.Class({
13 | Name: 'Services-Systemd-Settings',
14 | Extends: Gtk.Notebook,
15 |
16 | _init : function(params) {
17 | /*** Open Settings ***********************************************************************/
18 | this._settings = ExtensionUtils.getSettings();
19 | this._settings.connect('changed', Lang.bind(this, this._refresh));
20 |
21 | this._changedPermitted = false;
22 | /*****************************************************************************************/
23 |
24 |
25 |
26 | /*** GUI: General ************************************************************************/
27 | this.parent(params);
28 | this.set_tab_pos(Gtk.PositionType.TOP);
29 |
30 | let servicesPage = new Gtk.Grid()
31 | servicesPage.set_orientation(Gtk.Orientation.VERTICAL);
32 | servicesPage.margin = 20;
33 |
34 | let otherPage = new Gtk.Grid()
35 | otherPage.set_orientation(Gtk.Orientation.VERTICAL);
36 | otherPage.margin = 20;
37 | otherPage.set_row_spacing(10);
38 |
39 | //this.insert_page(servicesPage, new Gtk.Label("test"), 0)
40 | this.append_page(servicesPage, new Gtk.Label({ label: "Services" }))
41 | this.append_page(otherPage, new Gtk.Label({ label: "Other" }))
42 | /*****************************************************************************************/
43 |
44 |
45 |
46 | /*** GUI: Other Settings *****************************************************************/
47 | let showAddLabel = new Gtk.Label({
48 | label: "Show add services: ",
49 | xalign: 0,
50 | hexpand: true
51 | });
52 |
53 | this._showAddCheckbox = new Gtk.Switch();
54 | this._showAddCheckbox.set_halign(Gtk.Align.CENTER);
55 | this._showAddCheckbox.set_valign(Gtk.Align.CENTER);
56 | this._showAddCheckbox.connect('notify::active', Lang.bind(this, function(button) {
57 | this._changedPermitted = false;
58 | this._settings.set_boolean('show-add', button.active);
59 | this._changedPermitted = true;
60 | }));
61 |
62 | otherPage.attach(showAddLabel, 1, 1, 1, 1);
63 | otherPage.attach_next_to(this._showAddCheckbox, showAddLabel, 1, 1, 1);
64 |
65 |
66 |
67 | let showRestartLabel = new Gtk.Label({
68 | label: "Show restart button: ",
69 | xalign: 0,
70 | hexpand: true
71 | });
72 |
73 | this._showRestartCheckbox = new Gtk.Switch();
74 | this._showRestartCheckbox.set_halign(Gtk.Align.CENTER);
75 | this._showRestartCheckbox.set_valign(Gtk.Align.CENTER);
76 | this._showRestartCheckbox.connect('notify::active', Lang.bind(this, function(button) {
77 | this._changedPermitted = false;
78 | this._settings.set_boolean('show-restart', button.active);
79 | this._changedPermitted = true;
80 | }));
81 |
82 | otherPage.attach(showRestartLabel, 1, 2, 1, 1);
83 | otherPage.attach_next_to(this._showRestartCheckbox, showRestartLabel, 1, 1, 1);
84 |
85 |
86 | let positionLabel = new Gtk.Label({
87 | label: "Position: ",
88 | xalign: 0,
89 | hexpand: true
90 | });
91 |
92 | let model = new Gtk.ListStore();
93 | model.set_column_types([GObject.TYPE_INT, GObject.TYPE_STRING]);
94 |
95 | this._positionCombo = new Gtk.ComboBox({model: model});
96 | this._positionCombo.get_style_context().add_class(Gtk.STYLE_CLASS_RAISED);
97 |
98 | let renderer = new Gtk.CellRendererText();
99 | this._positionCombo.pack_start(renderer, true);
100 | this._positionCombo.add_attribute(renderer, 'text', 1);
101 |
102 | let positionsItems = [
103 | { id: 0, name: "Panel"},
104 | { id: 1, name: "Menu"}
105 | ]
106 | for (let i = 0; i < positionsItems.length; i++) {
107 | let item = positionsItems[i];
108 | let iter = model.append();
109 | model.set(iter, [0, 1], [item.id, item.name]);
110 | }
111 |
112 | this._positionCombo.connect('changed', Lang.bind(this, function(entry) {
113 | let [success, iter] = this._positionCombo.get_active_iter()
114 | if (success) {
115 | this._changedPermitted = false;
116 | this._settings.set_enum('position', this._positionCombo.get_model().get_value(iter, 0));
117 | this._changedPermitted = true;
118 | }
119 | }));
120 |
121 | otherPage.attach(positionLabel, 1, 3, 1, 1);
122 | otherPage.attach_next_to(this._positionCombo, positionLabel, 1, 1, 1);
123 |
124 |
125 | let commandMethodLabel = new Gtk.Label({
126 | label: "Command Method: ",
127 | xalign: 0,
128 | hexpand: true
129 | });
130 |
131 | let commandMethodModel = new Gtk.ListStore();
132 | commandMethodModel.set_column_types([GObject.TYPE_INT, GObject.TYPE_STRING]);
133 |
134 | this._commandMethodCombo = new Gtk.ComboBox({model: commandMethodModel});
135 | this._commandMethodCombo.get_style_context().add_class(Gtk.STYLE_CLASS_RAISED);
136 |
137 | let commandMethodRenderer = new Gtk.CellRendererText();
138 | this._commandMethodCombo.pack_start(commandMethodRenderer, true);
139 | this._commandMethodCombo.add_attribute(commandMethodRenderer, 'text', 1);
140 |
141 | let commandMethodsItems = [
142 | { id: 0, name: "pkexec" },
143 | { id: 1, name: "systemctl"},
144 | { id: 2, name: "sudo"}
145 | ]
146 |
147 | for (let i = 0; i < commandMethodsItems.length; i++) {
148 | let item = commandMethodsItems[i];
149 | let iter = commandMethodModel.append();
150 | commandMethodModel.set(iter, [0, 1], [item.id, item.name]);
151 | }
152 |
153 | this._commandMethodCombo.connect('changed', Lang.bind(this, function(entry) {
154 | let [success, iter] = this._commandMethodCombo.get_active_iter()
155 | if (success) {
156 | this._changedPermitted = false;
157 | this._settings.set_enum('command-method', this._commandMethodCombo.get_model().get_value(iter, 0));
158 | this._changedPermitted = true;
159 | }
160 | }));
161 |
162 | otherPage.attach(commandMethodLabel, 1, 4, 1, 1);
163 | otherPage.attach_next_to(this._commandMethodCombo, commandMethodLabel, 1, 1, 1);
164 | /*****************************************************************************************/
165 |
166 |
167 |
168 | /*** GUI: Services Settings **************************************************************/
169 | // Label
170 | let treeViewLabel = new Gtk.Label({ label: '' + "Listed systemd Services:" + '',
171 | use_markup: true,
172 | halign: Gtk.Align.START })
173 | servicesPage.add(treeViewLabel);
174 |
175 |
176 | // TreeView
177 | this._store = new Gtk.ListStore();
178 | this._store.set_column_types([GObject.TYPE_STRING, GObject.TYPE_STRING, GObject.TYPE_STRING]);
179 |
180 | this._treeView = new Gtk.TreeView({ model: this._store,
181 | hexpand: true, vexpand: true });
182 |
183 | let selection = this._treeView.get_selection();
184 | selection.set_mode(Gtk.SelectionMode.SINGLE);
185 | selection.connect ('changed', Lang.bind (this, this._onSelectionChanged));
186 |
187 |
188 | let labelColumn = new Gtk.TreeViewColumn({ expand: true,
189 | title: "Label" });
190 |
191 | let labelRenderer = new Gtk.CellRendererText;
192 | labelColumn.pack_start(labelRenderer, true);
193 | labelColumn.add_attribute(labelRenderer, "text", 0);
194 | this._treeView.append_column(labelColumn);
195 |
196 | let serviceColumn = new Gtk.TreeViewColumn({ expand: true,
197 | title: "Service" });
198 |
199 | let serviceRenderer = new Gtk.CellRendererText;
200 | serviceColumn.pack_start(serviceRenderer, true);
201 | serviceColumn.add_attribute(serviceRenderer, "text", 1);
202 | this._treeView.append_column(serviceColumn);
203 |
204 | let typeColumn = new Gtk.TreeViewColumn({ expand: true,
205 | title: "Type" });
206 |
207 | let typeRenderer = new Gtk.CellRendererText;
208 | typeColumn.pack_start(typeRenderer, true);
209 | typeColumn.add_attribute(typeRenderer, "text", 2);
210 | this._treeView.append_column(typeColumn);
211 |
212 | servicesPage.add(this._treeView);
213 |
214 | // Delete Toolbar
215 | let toolbar = new Gtk.Toolbar();
216 | toolbar.get_style_context().add_class(Gtk.STYLE_CLASS_INLINE_TOOLBAR);
217 | toolbar.halign = 2;
218 | servicesPage.add(toolbar);
219 |
220 | let upButton = new Gtk.ToolButton({ stock_id: Gtk.STOCK_GO_UP });
221 | upButton.connect('clicked', Lang.bind(this, this._up));
222 | toolbar.add(upButton);
223 |
224 | let downButton = new Gtk.ToolButton({ stock_id: Gtk.STOCK_GO_DOWN });
225 | downButton.connect('clicked', Lang.bind(this, this._down));
226 | toolbar.add(downButton);
227 |
228 | let delButton = new Gtk.ToolButton({ stock_id: Gtk.STOCK_DELETE });
229 | delButton.connect('clicked', Lang.bind(this, this._delete));
230 | toolbar.add(delButton);
231 |
232 | this._selDepButtons = [upButton, downButton, delButton]
233 |
234 | // Add Grid
235 | let grid = new Gtk.Grid();
236 |
237 | //// Label
238 | let labelName = new Gtk.Label({label: "Label: "});
239 | labelName.halign = 2;
240 |
241 | this._displayName = new Gtk.Entry({ hexpand: true,
242 | margin_top: 5 });
243 | this._displayName.set_placeholder_text("Name in menu");
244 |
245 | let labelService = new Gtk.Label({label: "Service: "});
246 | labelService.halign = 2;
247 |
248 | let sListStore = new Gtk.ListStore();
249 | sListStore.set_column_types([GObject.TYPE_STRING, GObject.TYPE_STRING, GObject.TYPE_STRING]);
250 |
251 | let types = ['system', 'user']
252 |
253 | this._availableSystemdServices = {
254 | 'all': []
255 | }
256 |
257 | for (let t in types) {
258 | let type = types[t]
259 | this._availableSystemdServices[type] = this._getSystemdServicesList(type)
260 | this._availableSystemdServices['all'] = this._availableSystemdServices['all'].concat(this._availableSystemdServices[type])
261 | for (let i in this._availableSystemdServices[type]) {
262 | let name = this._availableSystemdServices[type][i] + " " + type
263 | log(name)
264 | sListStore.set(sListStore.append(), [0, 1, 2], [name, this._availableSystemdServices[type][i], type]);
265 | }
266 | }
267 |
268 | this._systemName = new Gtk.Entry()
269 | this._systemName.set_placeholder_text("Systemd service name and type");
270 | let completion = new Gtk.EntryCompletion()
271 | this._systemName.set_completion(completion)
272 | completion.set_model(sListStore)
273 |
274 | completion.set_text_column(0)
275 |
276 | grid.attach(labelName, 1, 1, 1, 1);
277 | grid.attach_next_to(this._displayName, labelName, 1, 1, 1);
278 |
279 | grid.attach(labelService, 1, 2, 1, 1);
280 | grid.attach_next_to(this._systemName, labelService, 1, 1, 1);
281 |
282 | servicesPage.add(grid);
283 |
284 | let addToolbar = new Gtk.Toolbar();
285 | addToolbar.get_style_context().add_class(Gtk.STYLE_CLASS_INLINE_TOOLBAR);
286 | addToolbar.halign = 2;
287 | servicesPage.add(addToolbar);
288 |
289 | let addButton = new Gtk.ToolButton({
290 | stock_id: Gtk.STOCK_ADD,
291 | label: "Add",
292 | is_important: true
293 | });
294 |
295 | addButton.connect('clicked', Lang.bind(this, this._add));
296 | addToolbar.add(addButton);
297 | /*****************************************************************************************/
298 |
299 |
300 |
301 | this._changedPermitted = true;
302 | this._refresh();
303 | this._onSelectionChanged();
304 | },
305 | _getSystemdServicesList: function(type) {
306 | let [_u1, out_u1, err_u1, stat_u1] = GLib.spawn_command_line_sync('sh -c "systemctl --' + type + ' list-unit-files --type=service,timer --no-legend | awk \'{print $1}\'"');
307 | let allFiltered = out_u1.toString().split("\n");
308 | //let allFiltered = ByteArray.toString(out_u1).split("\n");
309 | let [_u2, out_u2, err_u2, stat_u2] = GLib.spawn_command_line_sync('sh -c "systemctl --' + type + ' list-units --type=service,timer --no-legend | awk \'{print $1}\'"');
310 | allFiltered = allFiltered.concat(out_u2.toString().split("\n"));
311 | //allFiltered = allFiltered.concat(ByteArray.toString(out_u2).split("\n"));
312 | return allFiltered.sort(
313 | function (a, b) {
314 | return a.toLowerCase().localeCompare(b.toLowerCase());
315 | })
316 | },
317 | _getTypeOfService: function(service) {
318 | let type = "undefined"
319 | if (this._availableSystemdServices['system'].indexOf(service) != -1)
320 | type = "system"
321 | else if (this._availableSystemdServices['user'].indexOf(service) != -1)
322 | type = "user"
323 | return type
324 | },
325 | _getIdFromIter: function(iter) {
326 | let displayName = this._store.get_value(iter, 0);
327 | let serviceName = this._store.get_value(iter, 1);
328 | let type = this._store.get_value(iter, 2);
329 | return JSON.stringify({"name": displayName, "service": serviceName, "type": type});
330 | },
331 | _isValidTemplateInstance: function(serviceName, type) {
332 | // is this a possible template instance
333 | let index = serviceName.indexOf("@");
334 | let result = index != -1;
335 |
336 | if (result) {
337 | let templateName = serviceName.substr(0,index+1) + ".service";
338 | // type is valid and template exists
339 | result = result && (type == "system" || type == "user") && (this._availableSystemdServices[type].indexOf(templateName) != -1);
340 | }
341 | return result;
342 | },
343 | _add: function() {
344 | let displayName = this._displayName.text.trim()
345 | let serviceEntry = this._systemName.text.trim()
346 | if (displayName.length > 0 && serviceEntry.length > 0) {
347 | let serviceArray = serviceEntry.split(" ")
348 | let serviceName = ""
349 | let type = ""
350 | if (serviceArray.length > 1) {
351 | serviceName = serviceArray[0]
352 | type = serviceArray[1]
353 | } else {
354 | serviceName = serviceArray[0]
355 | type = this._getTypeOfService(serviceName)
356 | }
357 |
358 | if (!this._isValidTemplateInstance(serviceName, type) && ( !(type == "system" || type == "user") || this._availableSystemdServices[type].indexOf(serviceName) == -1)) {
359 | this._messageDialog = new Gtk.MessageDialog ({
360 | title: "Warning",
361 | modal: true,
362 | buttons: Gtk.ButtonsType.OK,
363 | message_type: Gtk.MessageType.WARNING,
364 | text: "Service does not exist."
365 | });
366 | this._messageDialog.connect('response', Lang.bind(this, function() {
367 | this._messageDialog.close();
368 | }));
369 | this._messageDialog.show();
370 | } else {
371 | let id = JSON.stringify({"name": displayName, "service": serviceName, "type": type})
372 | let currentItems = this._settings.get_strv("systemd");
373 | let index = currentItems.indexOf(id);
374 | if (index < 0) {
375 | this._changedPermitted = false;
376 | currentItems.push(id);
377 | this._settings.set_strv("systemd", currentItems);
378 | this._store.set(this._store.append(), [0, 1, 2], [displayName, serviceName, type]);
379 | this._changedPermitted = true;
380 | }
381 | this._displayName.text = ""
382 | this._systemName.text = ""
383 | }
384 |
385 | } else {
386 | this._messageDialog = new Gtk.MessageDialog ({
387 | //parent: this.get_toplevel(),
388 | title: "Warning",
389 | modal: true,
390 | buttons: Gtk.ButtonsType.OK,
391 | message_type: Gtk.MessageType.WARNING,
392 | text: "No label and/or service specified."
393 | });
394 |
395 | this._messageDialog.connect ('response', Lang.bind(this, function() {
396 | this._messageDialog.close();
397 | }));
398 | this._messageDialog.show();
399 | }
400 | },
401 | _up: function() {
402 | let [any, model, iter] = this._treeView.get_selection().get_selected();
403 |
404 | if (any) {
405 | let index = this._settings.get_strv("systemd").indexOf(this._getIdFromIter(iter));
406 | this._move(index, index - 1)
407 | }
408 | },
409 | _down: function() {
410 | let [any, model, iter] = this._treeView.get_selection().get_selected();
411 |
412 | if (any) {
413 | let index = this._settings.get_strv("systemd").indexOf(this._getIdFromIter(iter));
414 | this._move(index, index + 1)
415 | }
416 | },
417 | _move: function(oldIndex, newIndex) {
418 | let currentItems = this._settings.get_strv("systemd");
419 |
420 | if (oldIndex < 0 || oldIndex >= currentItems.length ||
421 | newIndex < 0 || newIndex >= currentItems.length)
422 | return;
423 |
424 | currentItems.splice(newIndex, 0, currentItems.splice(oldIndex, 1)[0]);
425 |
426 | this._settings.set_strv("systemd", currentItems);
427 |
428 | this._treeView.get_selection().unselect_all();
429 | this._treeView.get_selection().select_path(Gtk.TreePath.new_from_string(String(newIndex)));
430 | },
431 | _delete: function() {
432 | let [any, model, iter] = this._treeView.get_selection().get_selected();
433 |
434 | if (any) {
435 | let currentItems = this._settings.get_strv("systemd");
436 | let index = currentItems.indexOf(this._getIdFromIter(iter));
437 |
438 | if (index < 0)
439 | return;
440 |
441 | currentItems.splice(index, 1);
442 | this._settings.set_strv("systemd", currentItems);
443 |
444 | this._store.remove(iter);
445 | }
446 | },
447 | _onSelectionChanged: function() {
448 | let [any, model, iter] = this._treeView.get_selection().get_selected();
449 | if (any) {
450 | this._selDepButtons.forEach(function(value) {
451 | value.set_sensitive(true)
452 | });
453 | } else {
454 | this._selDepButtons.forEach(function(value) {
455 | value.set_sensitive(false)
456 | });
457 | }
458 | },
459 | _refresh: function() {
460 | if (!this._changedPermitted)
461 | return;
462 |
463 | this._store.clear();
464 | this._showAddCheckbox.set_active(this._settings.get_boolean('show-add'))
465 | this._showRestartCheckbox.set_active(this._settings.get_boolean('show-restart'))
466 | this._positionCombo.set_active(this._settings.get_enum('position'))
467 | this._commandMethodCombo.set_active(this._settings.get_enum('command-method'))
468 |
469 | let currentItems = this._settings.get_strv("systemd");
470 | let validItems = [ ];
471 |
472 | for (let i = 0; i < currentItems.length; i++) {
473 | let entry = JSON.parse(currentItems[i]);
474 | // REMOVE NOT EXISTING ENTRIES
475 | if (!this._isValidTemplateInstance(entry["service"],entry["type"]) && (this._availableSystemdServices["all"].indexOf(entry["service"]) < 0))
476 | continue;
477 |
478 | // COMPATIBILITY
479 | if(!("type" in entry))
480 | entry["type"] = this._getTypeOfService(entry["service"])
481 |
482 | validItems.push(JSON.stringify(entry));
483 |
484 | let iter = this._store.append();
485 | this._store.set(
486 | iter,
487 | [0, 1, 2],
488 | [entry["name"], entry["service"], entry["type"]]
489 | );
490 | }
491 |
492 | this._changedPermitted = false
493 | this._settings.set_strv("systemd", validItems);
494 | this._changedPermitted = true
495 | }
496 | });
497 |
498 | function init() {
499 | }
500 |
501 | function buildPrefsWidget() {
502 | let widget = new ServicesSystemdSettings();
503 | widget.show_all();
504 |
505 | return widget;
506 | }
507 |
--------------------------------------------------------------------------------
/services-systemd@abteil.org/schemas/gschemas.compiled:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/petres/gnome-shell-extension-services-systemd/86936eff0f6f87d1f8f3b0270472881234064aa2/services-systemd@abteil.org/schemas/gschemas.compiled
--------------------------------------------------------------------------------
/services-systemd@abteil.org/schemas/org.gnome.shell.extensions.services-systemd.gschema.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | [ ]
15 | Systemd service list
16 | A list of serrvice which are shown
17 |
18 |
19 | true
20 | Show the add services button
21 | Whether to show the add services button as last entry in the extensions list.
22 |
23 |
24 | true
25 | Show the restart button for each service
26 | Whether to show the restart button for each service.
27 |
28 |
29 | 'panel'
30 | Where to display the extension indicator
31 |
32 |
33 | 'pkexec'
34 | The method to use to manage services
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/services-systemd@abteil.org/stylesheet.css:
--------------------------------------------------------------------------------
1 | .system-menu-action.services-systemd-button-reload {
2 | height: 18px;
3 | width: 18px;
4 | padding: 0px;
5 | border: 0px;
6 | }
--------------------------------------------------------------------------------