Line data Source code
1 : /*
2 : * Copyright (C) 2016 Red Hat, Inc.
3 : * SPDX-License-Identifier: LGPL-2.1-or-later
4 : */
5 :
6 2 : import cockpit from "cockpit";
7 :
8 2 : const _ = cockpit.gettext;
9 :
10 2 : export const client = {};
11 :
12 2 : const busName = "org.fedoraproject.Setroubleshootd";
13 2 : const dbusInterface = "org.fedoraproject.SetroubleshootdIface";
14 2 : const dbusPath = "/org/fedoraproject/Setroubleshootd";
15 :
16 2 : const busNameFixit = "org.fedoraproject.SetroubleshootFixit";
17 2 : const dbusInterfaceFixit = busNameFixit;
18 2 : const dbusPathFixit = "/org/fedoraproject/SetroubleshootFixit/object";
19 :
20 2 : client.init = function() {
21 2 : client.connected = false;
22 2 : const dbusClientSeTroubleshoot = cockpit.dbus(busName, { superuser: "try" });
23 2 : client.proxy = dbusClientSeTroubleshoot.proxy(dbusInterface, dbusPath);
24 :
25 2 : client.proxyFixit = cockpit.dbus(busNameFixit, { superuser: "try" }).proxy(dbusInterfaceFixit, dbusPathFixit);
26 :
27 2 : const connectPromise = new Promise((resolve, reject) => {
28 2 : client.proxy.wait(() => {
29 : // HACK setroubleshootd seems to drop connections if we don't start explicitly
30 2 : client.proxy.call("start", [])
31 2 : .then(() => {
32 2 : client.connected = true;
33 2 : resolve();
34 2 : })
35 0 : .catch(ex => reject(new Error(_("Unable to start setroubleshootd"))));
36 2 : });
37 2 : });
38 :
39 2 : client.alertCallback = null;
40 :
41 0 : function handleSignal(event, name, args) {
42 0 : if (client.alertCallback && name == "alert") {
43 0 : const level = args[0];
44 0 : const localId = args[1];
45 0 : client.alertCallback(level, localId);
46 0 : }
47 0 : }
48 :
49 : // register to receive calls whenever a new alert becomes available
50 : // signature for the alert callback: (level, localId)
51 2 : client.handleAlert = function(callback) {
52 : // if we didn't listen to events before, do so now
53 2 : if (!client.alertCallback) {
54 2 : client.proxy.addEventListener("signal", handleSignal);
55 2 : }
56 2 : client.alertCallback = callback;
57 2 : };
58 :
59 : // returns a promise
60 2 : client.getAlerts = function(since) {
61 2 : return new Promise((resolve, reject) => {
62 2 : let call;
63 2 : if (since !== undefined)
64 1 : call = client.proxy.call("get_all_alerts_since", [since]);
65 : else
66 2 : call = client.proxy.call("get_all_alerts", []);
67 2 : call
68 2 : .then(result => {
69 2 : resolve(result[0].map(entry => ({
70 2 : localId: entry[0],
71 2 : summary: entry[1],
72 2 : reportCount: entry[2],
73 2 : })));
74 2 : })
75 0 : .catch(ex => reject(ex));
76 2 : });
77 2 : };
78 :
79 : /* Return an alert with summary, audit events, fix suggestions (by id)
80 : localId: an alert id
81 : summary: a brief description of an alert. E.g.
82 : "SELinux is preventing /usr/bin/bash from ioctl access on the unix_stream_socket unix_stream_socket."
83 : reportCount: count of reports of this alert
84 : auditEvent: an array of audit events (AVC, SYSCALL) connected to the alert
85 : pluginAnalysis: an array of plugin analysis structure
86 : ifText
87 : thenText
88 : doText
89 : analysisId: plugin id. It can be used in org.fedoraproject.SetroubleshootFixit.run_fix()
90 : fixable: True when an alert is fixable by a plugin
91 : reportBug: True when an alert should be reported
92 : firstSeen: when the alert was seen for the first time, timestamp in ms
93 : lastSeen: when the alert was seen for the last time, timestamp in ms
94 : level: "green", "yellow" or "red"
95 : */
96 2 : client.getAlert = localId => client.proxy.call("get_alert", [localId])
97 2 : .then(result => {
98 2 : const details = {
99 2 : localId: result[0],
100 2 : summary: result[1],
101 2 : reportCount: result[2],
102 2 : auditEvent: result[3],
103 2 : pluginAnalysis: result[4],
104 2 : firstSeen: result[5] / 1000,
105 2 : lastSeen: result[6] / 1000,
106 2 : level: result[7],
107 2 : };
108 : // cleanup analysis
109 2 : details.pluginAnalysis = details.pluginAnalysis.map(itm => ({
110 2 : ifText: itm[0],
111 2 : thenText: itm[1],
112 2 : doText: itm[2],
113 2 : analysisId: itm[3],
114 2 : fixable: itm[4],
115 2 : reportBug: itm[5],
116 2 : }));
117 2 : return details;
118 2 : })
119 0 : .catch(ex => {
120 0 : console.warn("Unable to get alert for id", localId, ":", JSON.stringify(ex));
121 0 : return Promise.reject(new Error(`Unable to get alert for id ${localId}: ${ex.toString()}`));
122 0 : });
123 :
124 : /* Run a fix via SetroubleshootFixit
125 : The analysisId is given as part of pluginAnalysis entries in alert details
126 : */
127 0 : client.runFix = (alertId, analysisId) => client.proxyFixit.call("run_fix", [alertId, analysisId])
128 0 : .then(result => result[0])
129 0 : .catch(ex => {
130 0 : console.warn("Unable to run fix:", JSON.stringify(ex));
131 0 : return Promise.reject(new Error(cockpit.format(_("Unable to run fix: $0"), ex.toString())));
132 0 : });
133 :
134 : /* Delete an alert from the database (will be removed for all users), returns true on success
135 : * Only assign this to the client variable if the dbus interface actually supports the operation
136 : */
137 0 : client.deleteAlert = localId => client.proxy.call("delete_alert", [localId])
138 0 : .then(success => {
139 0 : if (!success)
140 0 : return Promise.reject(new Error(cockpit.format(_("Failed to delete alert: $0"), localId)));
141 0 : })
142 0 : .catch(ex => {
143 0 : console.warn("Unable to delete alert with id", localId, ":", JSON.stringify(ex));
144 0 : return Promise.reject(new Error(cockpit.format(_("Failed to delete alert: $0"), ex.toString())));
145 0 : });
146 :
147 : // connect to dbus and start setroubleshootd
148 2 : return connectPromise;
149 2 : };
|