• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mendersoftware / mender-connect / 1592022205

16 Dec 2024 02:11PM UTC coverage: 77.911% (+0.5%) from 77.427%
1592022205

push

gitlab-ci

web-flow
Merge pull request #145 from alfrunes/QA-764

refac: Migrate from gorilla/websocket to coder/websocket

19 of 28 new or added lines in 2 files covered. (67.86%)

4 existing lines in 1 file now uncovered.

2476 of 3178 relevant lines covered (77.91%)

6.86 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

65.05
/app/daemon_shell.go
1
// Copyright 2022 Northern.tech AS
2
//
3
//        Licensed under the Apache License, Version 2.0 (the "License");
4
//        you may not use this file except in compliance with the License.
5
//        You may obtain a copy of the License at
6
//
7
//            http://www.apache.org/licenses/LICENSE-2.0
8
//
9
//        Unless required by applicable law or agreed to in writing, software
10
//        distributed under the License is distributed on an "AS IS" BASIS,
11
//        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
//        See the License for the specific language governing permissions and
13
//        limitations under the License.
14
package app
15

16
import (
17
        "fmt"
18

19
        "github.com/mendersoftware/go-lib-micro/ws"
20
        wsshell "github.com/mendersoftware/go-lib-micro/ws/shell"
21
        "github.com/pkg/errors"
22
        log "github.com/sirupsen/logrus"
23

24
        "github.com/mendersoftware/mender-connect/config"
25
        "github.com/mendersoftware/mender-connect/procps"
26
        "github.com/mendersoftware/mender-connect/session"
27
        "github.com/mendersoftware/mender-connect/utils"
28
)
29

30
const (
31
        propertyTerminalHeight = "terminal_height"
32
        propertyTerminalWidth  = "terminal_width"
33
        propertyUserID         = "user_id"
34
)
35

36
func getUserIdFromMessage(message *ws.ProtoMsg) string {
7✔
37
        userID, _ := message.Header.Properties[propertyUserID].(string)
7✔
38
        return userID
7✔
39
}
7✔
40

41
func (d *MenderShellDaemon) routeMessageSpawnShell(message *ws.ProtoMsg) error {
8✔
42
        var err error
8✔
43
        response := &ws.ProtoMsg{
8✔
44
                Header: ws.ProtoHdr{
8✔
45
                        Proto:     message.Header.Proto,
8✔
46
                        MsgType:   message.Header.MsgType,
8✔
47
                        SessionID: message.Header.SessionID,
8✔
48
                        Properties: map[string]interface{}{
8✔
49
                                "status": wsshell.NormalMessage,
8✔
50
                        },
8✔
51
                },
8✔
52
                Body: []byte{},
8✔
53
        }
8✔
54
        if d.shellsSpawned >= config.MaxShellsSpawned {
9✔
55
                err = session.ErrSessionTooManyShellsAlreadyRunning
1✔
56
                d.routeMessageResponse(response, err)
1✔
57
                return err
1✔
58
        }
1✔
59
        s := session.MenderShellSessionGetById(message.Header.SessionID)
7✔
60
        if s == nil {
14✔
61
                userId := getUserIdFromMessage(message)
7✔
62
                if s, err = session.NewMenderShellSession(
7✔
63
                        message.Header.SessionID,
7✔
64
                        userId,
7✔
65
                        d.expireSessionsAfter,
7✔
66
                        d.expireSessionsAfterIdle,
7✔
67
                ); err != nil {
8✔
68
                        d.routeMessageResponse(response, err)
1✔
69
                        return err
1✔
70
                }
1✔
71
                log.Debugf("created a new session: %s", s.GetId())
6✔
72
        }
73

74
        response.Header.SessionID = s.GetId()
6✔
75

6✔
76
        terminalHeight := d.TerminalConfig.Height
6✔
77
        terminalWidth := d.TerminalConfig.Width
6✔
78

6✔
79
        requestedHeight, requestedWidth := mapPropertiesToTerminalHeightAndWidth(
6✔
80
                message.Header.Properties,
6✔
81
        )
6✔
82
        if requestedHeight > 0 && requestedWidth > 0 {
12✔
83
                terminalHeight = requestedHeight
6✔
84
                terminalWidth = requestedWidth
6✔
85
        }
6✔
86

87
        log.Debugf("starting shell session_id=%s", s.GetId())
6✔
88
        if err = s.StartShell(s.GetId(), session.MenderShellTerminalSettings{
6✔
89
                Uid:            uint32(d.uid),
6✔
90
                Gid:            uint32(d.gid),
6✔
91
                Shell:          d.shell,
6✔
92
                HomeDir:        d.homeDir,
6✔
93
                TerminalString: d.terminalString,
6✔
94
                Height:         terminalHeight,
6✔
95
                Width:          terminalWidth,
6✔
96
                ShellArguments: d.shellArguments,
6✔
97
        }); err != nil {
6✔
98
                err = errors.Wrap(err, "failed to start shell")
×
99
                d.routeMessageResponse(response, err)
×
100
                return err
×
101
        }
×
102

103
        log.Debug("Shell started")
6✔
104
        d.shellsSpawned++
6✔
105

6✔
106
        response.Body = []byte("Shell started")
6✔
107
        d.routeMessageResponse(response, err)
6✔
108
        return nil
6✔
109
}
110

111
func (d *MenderShellDaemon) routeMessageStopShell(message *ws.ProtoMsg) error {
4✔
112
        var err error
4✔
113
        response := &ws.ProtoMsg{
4✔
114
                Header: ws.ProtoHdr{
4✔
115
                        Proto:     message.Header.Proto,
4✔
116
                        MsgType:   message.Header.MsgType,
4✔
117
                        SessionID: message.Header.SessionID,
4✔
118
                        Properties: map[string]interface{}{
4✔
119
                                "status": wsshell.NormalMessage,
4✔
120
                        },
4✔
121
                },
4✔
122
                Body: []byte{},
4✔
123
        }
4✔
124

4✔
125
        if len(message.Header.SessionID) < 1 {
4✔
126
                userId := getUserIdFromMessage(message)
×
127
                if len(userId) < 1 {
×
128
                        err = errors.New("StopShellMessage: sessionId not given and userId empty")
×
129
                        d.routeMessageResponse(response, err)
×
130
                        return err
×
131
                }
×
132
                shellsStoppedCount, err := session.MenderShellStopByUserId(userId)
×
133
                if err == nil {
×
134
                        if shellsStoppedCount > d.shellsSpawned {
×
135
                                d.shellsSpawned = 0
×
136
                                err = errors.New(fmt.Sprintf("StopByUserId: the shells stopped count (%d) "+
×
137
                                        "greater than total shells spawned (%d). resetting shells "+
×
138
                                        "spawned to 0.", shellsStoppedCount, d.shellsSpawned))
×
139
                                d.routeMessageResponse(response, err)
×
140
                                return err
×
141
                        } else {
×
142
                                log.Debugf("StopByUserId: stopped %d shells.", shellsStoppedCount)
×
143
                                d.DecreaseSpawnedShellsCount(shellsStoppedCount)
×
144
                        }
×
145
                }
146
                d.routeMessageResponse(response, err)
×
147
                return err
×
148
        }
149

150
        s := session.MenderShellSessionGetById(message.Header.SessionID)
4✔
151
        if s == nil {
6✔
152
                err = errors.New(
2✔
153
                        fmt.Sprintf(
2✔
154
                                "routeMessage: StopShellMessage: session not found for id %s",
2✔
155
                                message.Header.SessionID,
2✔
156
                        ),
2✔
157
                )
2✔
158
                d.routeMessageResponse(response, err)
2✔
159
                return err
2✔
160
        }
2✔
161

162
        err = s.StopShell()
2✔
163
        if err != nil {
2✔
UNCOV
164
                if procps.ProcessExists(s.GetShellPid()) {
×
165
                        log.Errorf("could not terminate shell (pid %d) for session %s, user"+
×
166
                                "will not be able to start another one if the limit is reached.",
×
167
                                s.GetShellPid(),
×
168
                                s.GetId())
×
169
                        err = errors.New("could not terminate shell: " + err.Error() + ".")
×
170
                        d.routeMessageResponse(response, err)
×
171
                        return err
×
UNCOV
172
                } else {
×
UNCOV
173
                        log.Errorf("process error on exit: %s", err.Error())
×
UNCOV
174
                }
×
175
        }
176
        if d.shellsSpawned == 0 {
2✔
177
                log.Warn("can't decrement shellsSpawned count: it is 0.")
×
178
        } else {
2✔
179
                d.shellsSpawned--
2✔
180
        }
2✔
181
        err = session.MenderShellDeleteById(s.GetId())
2✔
182
        d.routeMessageResponse(response, err)
2✔
183
        return err
2✔
184
}
185

186
func (d *MenderShellDaemon) routeMessageShellCommand(message *ws.ProtoMsg) error {
3✔
187
        var err error
3✔
188
        response := &ws.ProtoMsg{
3✔
189
                Header: ws.ProtoHdr{
3✔
190
                        Proto:     message.Header.Proto,
3✔
191
                        MsgType:   message.Header.MsgType,
3✔
192
                        SessionID: message.Header.SessionID,
3✔
193
                        Properties: map[string]interface{}{
3✔
194
                                "status": wsshell.NormalMessage,
3✔
195
                        },
3✔
196
                },
3✔
197
                Body: []byte{},
3✔
198
        }
3✔
199

3✔
200
        s := session.MenderShellSessionGetById(message.Header.SessionID)
3✔
201
        if s == nil {
4✔
202
                err = session.ErrSessionNotFound
1✔
203
                d.routeMessageResponse(response, err)
1✔
204
                return err
1✔
205
        }
1✔
206
        err = s.ShellCommand(message)
2✔
207
        if err != nil {
2✔
208
                err = errors.Wrapf(
×
209
                        err,
×
210
                        "routeMessage: shell command execution error, session_id=%s",
×
211
                        message.Header.SessionID,
×
212
                )
×
213
                d.routeMessageResponse(response, err)
×
214
                return err
×
215
        }
×
216
        return nil
2✔
217
}
218

219
func mapPropertiesToTerminalHeightAndWidth(properties map[string]interface{}) (uint16, uint16) {
6✔
220
        var terminalHeight, terminalWidth uint16
6✔
221
        requestedHeight, requestedHeightOk := properties[propertyTerminalHeight]
6✔
222
        requestedWidth, requestedWidthOk := properties[propertyTerminalWidth]
6✔
223
        if requestedHeightOk && requestedWidthOk {
12✔
224
                if val, _ := utils.Num64(requestedHeight); val > 0 {
12✔
225
                        terminalHeight = uint16(val)
6✔
226
                }
6✔
227
                if val, _ := utils.Num64(requestedWidth); val > 0 {
12✔
228
                        terminalWidth = uint16(val)
6✔
229
                }
6✔
230
        }
231
        return terminalHeight, terminalWidth
6✔
232
}
233

234
func (d *MenderShellDaemon) routeMessageShellResize(message *ws.ProtoMsg) error {
×
235
        var err error
×
236

×
237
        s := session.MenderShellSessionGetById(message.Header.SessionID)
×
238
        if s == nil {
×
239
                err = session.ErrSessionNotFound
×
240
                log.Errorf(err.Error())
×
241
                return err
×
242
        }
×
243

244
        terminalHeight, terminalWidth := mapPropertiesToTerminalHeightAndWidth(
×
245
                message.Header.Properties,
×
246
        )
×
247
        if terminalHeight > 0 && terminalWidth > 0 {
×
248
                s.ResizeShell(terminalHeight, terminalWidth)
×
249
        }
×
250
        return nil
×
251
}
252

253
func (d *MenderShellDaemon) routeMessagePongShell(message *ws.ProtoMsg) error {
×
254
        var err error
×
255

×
256
        s := session.MenderShellSessionGetById(message.Header.SessionID)
×
257
        if s == nil {
×
258
                err = session.ErrSessionNotFound
×
259
                log.Errorf(err.Error())
×
260
                return err
×
261
        }
×
262

263
        s.HealthcheckPong()
×
264
        return nil
×
265
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc