Skip to content
GitLab
Explore
Sign in
Register
Primary navigation
Search or go to…
Project
S
SteamOS Devkit Service
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
devkit
SteamOS Devkit Service
Commits
19673648
Commit
19673648
authored
3 years ago
by
Timothee Besset
Browse files
Options
Downloads
Patches
Plain Diff
devkit_utils module
parent
697dd9fc
No related branches found
No related tags found
No related merge requests found
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
hooks/approve-ssh-key
+4
-8
4 additions, 8 deletions
hooks/approve-ssh-key
hooks/devkit_utils/__init__.py
+265
-0
265 additions, 0 deletions
hooks/devkit_utils/__init__.py
with
269 additions
and
8 deletions
hooks/approve-ssh-key
+
4
−
8
View file @
19673648
...
...
@@ -11,11 +11,7 @@ ENABLE_SSHD = '/usr/bin/steamos-polkit-helpers/steamos-enable-sshd'
logger
=
logging
.
getLogger
(
os
.
path
.
realpath
(
__file__
))
try
:
import
steam_devkit.utils
except
:
sys
.
path
.
append
(
os
.
path
.
abspath
(
os
.
path
.
join
(
os
.
path
.
dirname
(
__file__
),
'
..
'
)))
import
steam_devkit.utils
import
devkit_utils
if
__name__
==
'
__main__
'
:
logger
.
setLevel
(
logging
.
INFO
)
...
...
@@ -44,7 +40,7 @@ if __name__ == '__main__':
key_identifier
=
request
.
split
(
'
'
)[
2
]
request
=
f
'
Development host at IP
{
requestIP
}
key:
{
key_identifier
!r}
'
steam_
devkit
.
utils
.
validate_steam_client
()
devkit
_
utils
.
validate_steam_client
()
with
tempfile
.
TemporaryDirectory
(
prefix
=
'
approve-ssh-key
'
)
as
tempdir
:
logger
.
debug
(
'
Issue approve-ssh-key request to the running Steam client
'
)
...
...
@@ -53,8 +49,8 @@ if __name__ == '__main__':
urllib_quote_plus
(
response
),
urllib_quote_plus
(
request
),
)
steam_
devkit
.
utils
.
execute_steam_client_command
(
cmd
)
with
steam_
devkit
.
utils
.
wait_on_file_response
(
response
,
timeout
=
30
)
as
f
:
devkit
_
utils
.
execute_steam_client_command
(
cmd
)
with
devkit
_
utils
.
wait_on_file_response
(
response
,
timeout
=
30
)
as
f
:
logger
.
debug
(
'
Got response from Steam client
'
)
# Make sure sshd is enabled to support development features
if
os
.
path
.
exists
(
ENABLE_SSHD
):
...
...
This diff is collapsed.
Click to expand it.
hooks/devkit_utils/__init__.py
0 → 100644
+
265
−
0
View file @
19673648
#!/usr/bin/env python
# encoding: utf-8
"""
Utility functions for the Steam client hook scripts
"""
import
sys
import
os
import
traceback
import
tempfile
import
json
import
logging
import
fcntl
import
errno
import
contextlib
import
time
import
fcntl
import
logging
as
logging_module
logger
=
logging_module
.
getLogger
(
__name__
)
@contextlib.contextmanager
def
wrap_outputs
(
stderr_prefix
):
# capture stderr to file to support debugging
stderr_fd
=
sys
.
stderr
.
fileno
()
tf
=
tempfile
.
NamedTemporaryFile
(
mode
=
'
w+
'
,
prefix
=
stderr_prefix
,
delete
=
True
)
if
sys
.
version_info
>=
(
3
,
4
):
# this API in the os module is only available for python3
# but it does not seem to work with subprocess anyway
os
.
set_inheritable
(
tf
.
file
.
fileno
(),
True
)
assert
os
.
get_inheritable
(
tf
.
file
.
fileno
())
sys
.
stderr
=
tf
.
file
# we can only write out a json response to stdout,
# so redirect stdout to stderr,
# and keep a handle on the original stdout for the response
stdout_fd
=
os
.
dup
(
sys
.
stdout
.
fileno
())
os
.
dup2
(
sys
.
stderr
.
fileno
(),
sys
.
stdout
.
fileno
())
ctx
=
{}
try
:
yield
ctx
except
:
logger
.
error
(
traceback
.
format_exc
())
finally
:
tf
.
flush
()
tf
.
seek
(
0
)
os
.
write
(
stderr_fd
,
tf
.
read
().
encode
(
'
utf-8
'
))
if
'
ret
'
in
ctx
:
os
.
write
(
stdout_fd
,
json
.
dumps
(
ctx
[
'
ret
'
]).
encode
(
'
utf-8
'
))
class
SteamClientNotRunningException
(
Exception
):
def
__init__
(
self
,
error_message
):
self
.
error_message
=
error_message
def
__str__
(
self
):
return
self
.
error_message
def
validate_steam_client
():
"""
Verify that the steam client is running, and permissions are adequate
"""
pid_path
=
os
.
path
.
normpath
(
os
.
path
.
realpath
(
os
.
path
.
expanduser
(
'
~/.steam/steam.pid
'
)))
if
not
os
.
path
.
exists
(
pid_path
):
raise
SteamClientNotRunningException
(
'
{0} does not exist
'
.
format
(
pid_path
))
try
:
pid
=
int
(
open
(
pid_path
,
'
rt
'
).
read
())
except
Exception
:
raise
SteamClientNotRunningException
(
'
{0} is invalid
'
.
format
(
pid_path
))
try
:
os
.
kill
(
pid
,
0
)
except
OSError
:
raise
SteamClientNotRunningException
(
'
{0} does not refer to a valid process
'
.
format
(
pid_path
))
logger
.
info
(
'
Found steam client pid %s
'
,
pid
)
def
execute_steam_client_command
(
cmd
):
"""
Send a command to the steam client over the IPC pipe
"""
pipe_path
=
os
.
path
.
normpath
(
os
.
path
.
realpath
(
os
.
path
.
expanduser
(
'
~/.steam/steam.pipe
'
)))
try
:
pipe
=
open
(
pipe_path
,
'
wb+
'
,
0
)
except
IOError
:
raise
Exception
(
'
cannot open steam client pipe
'
)
session_token
=
open
(
os
.
path
.
expanduser
(
'
~/.steam/steam.token
'
)).
read
()
pipe_cmd
=
'
devkit-1 steam://devkit-1/{0}/{1}
'
.
format
(
session_token
,
cmd
)
logger
.
debug
(
'
Sending command line:
'
)
logger
.
debug
(
pipe_cmd
)
pipe
.
write
(
'
{0}
\n
'
.
format
(
pipe_cmd
).
encode
(
'
utf-8
'
))
pipe
.
close
()
def
save_argv
(
gameid
,
argv
):
"""
Save command line and arguments if provided
"""
if
argv
is
None
:
return
argvfile
=
os
.
path
.
join
(
os
.
getenv
(
"
HOME
"
),
"
devkit-game
"
,
gameid
+
"
-argv.json
"
)
try
:
with
open
(
argvfile
,
"
w
"
)
as
argvf
:
fcntl
.
flock
(
argvf
,
fcntl
.
LOCK_EX
)
json
.
dump
(
argv
,
argvf
)
fcntl
.
flock
(
argvf
,
fcntl
.
LOCK_UN
)
except
IOError
:
raise
Exception
(
"
Unable to open argv file for writing: {0}
"
.
format
(
argvfile
))
def
obtain_argv
(
gameid
,
argv
):
"""
Obtain command line with arguments
"""
# If present and not None or [], just return the local arguments
if
argv
:
return
argv
# From here, expect arguments to have been saved previously
argvfile
=
os
.
path
.
join
(
os
.
getenv
(
"
HOME
"
),
"
devkit-game
"
,
gameid
+
"
-argv.json
"
)
try
:
with
open
(
argvfile
,
"
r
"
)
as
argvf
:
fcntl
.
flock
(
argvf
,
fcntl
.
LOCK_EX
)
argv
=
json
.
load
(
argvf
)
fcntl
.
flock
(
argvf
,
fcntl
.
LOCK_UN
)
except
IOError
:
raise
Exception
(
"
Unable to open argv file for reading: {0}
"
.
format
(
argvfile
))
return
argv
def
save_settings
(
gameid
,
data
):
"""
Save settings
"""
settingsfile
=
os
.
path
.
join
(
os
.
getenv
(
"
HOME
"
),
"
devkit-game
"
,
gameid
+
"
-settings.json
"
)
settings
=
dict
()
if
data
.
get
(
'
clear_settings
'
,
False
):
settings
=
{}
else
:
try
:
with
open
(
settingsfile
,
"
r
"
)
as
f
:
fcntl
.
flock
(
f
,
fcntl
.
LOCK_EX
)
settings
=
json
.
load
(
f
)
fcntl
.
flock
(
f
,
fcntl
.
LOCK_UN
)
except
IOError
as
e
:
if
(
e
.
errno
!=
errno
.
ENOENT
):
raise
# Merge settings from new json
if
'
settings
'
in
data
:
settings
.
update
(
data
[
'
settings
'
])
try
:
with
open
(
settingsfile
,
"
w
"
)
as
f
:
fcntl
.
flock
(
f
,
fcntl
.
LOCK_EX
)
json
.
dump
(
settings
,
f
)
fcntl
.
flock
(
f
,
fcntl
.
LOCK_UN
)
except
(
IOError
):
raise
Exception
(
"
Unable to open settings file for writing: {0}
"
.
format
(
settingsfile
))
return
settings
def
load_settings
(
gameid
):
settingsfile
=
os
.
path
.
join
(
os
.
getenv
(
"
HOME
"
),
"
devkit-game
"
,
gameid
+
'
-settings.json
'
)
if
not
os
.
path
.
isfile
(
settingsfile
):
return
None
with
open
(
settingsfile
,
"
r
"
)
as
f
:
fcntl
.
flock
(
f
,
fcntl
.
LOCK_EX
)
settings
=
json
.
load
(
f
)
fcntl
.
flock
(
f
,
fcntl
.
LOCK_UN
)
return
settings
class
SteamResponse_Timeout
(
Exception
):
pass
class
SteamResponse_Error
(
Exception
):
def
__init__
(
self
,
error_response
):
self
.
error_response
=
error_response
def
__str__
(
self
):
return
self
.
error_response
@contextlib.contextmanager
def
wait_on_file_response
(
path
,
timeout
=
5
):
"""
The pipe to the Steam Client is one way.
Responses from the Steam Client are written to filesystem.
Protocol is as follows:
- Steam Client creates a
'
path.lock
'
file
- Steam Client writes either
'
path
'
or
'
path.error
'
to indicate a problem
- Steam Client deletes
'
path.lock
'
- Caller (us) can then read the response
NOTE 1: this function is used as a context manager and will block until a response comes in or timeout.
NOTE 2: the files are created by Steam when responding to a command. If the files already exist the response protocol will break.
"""
lock_path
=
'
{0}.lock
'
.
format
(
path
)
error_path
=
'
{0}.error
'
.
format
(
path
)
max_count
=
timeout
while
True
:
time
.
sleep
(
1
)
if
os
.
path
.
exists
(
error_path
)
or
os
.
path
.
exists
(
path
)
and
not
os
.
path
.
exists
(
lock_path
):
if
os
.
path
.
exists
(
error_path
):
with
open
(
error_path
,
'
r
'
)
as
f
:
fcntl
.
flock
(
f
,
fcntl
.
LOCK_EX
)
error_response
=
f
.
read
()
fcntl
.
flock
(
f
,
fcntl
.
LOCK_UN
)
raise
SteamResponse_Error
(
error_response
)
with
open
(
path
,
'
r
'
)
as
f
:
fcntl
.
flock
(
f
,
fcntl
.
LOCK_EX
)
success_response
=
f
.
read
()
yield
success_response
fcntl
.
flock
(
f
,
fcntl
.
LOCK_UN
)
return
max_count
-=
1
if
max_count
>
0
:
continue
raise
SteamResponse_Timeout
()
# Setting up as a context manager so we never miss the deletion
# Creating a temporary .lock file to guard the create operation
@contextlib.contextmanager
def
create_pid
(
pid_path
):
os
.
makedirs
(
os
.
path
.
dirname
(
pid_path
),
exist_ok
=
True
)
lock_path
=
'
{0}.lock
'
.
format
(
pid_path
)
try
:
lock_file
=
os
.
open
(
lock_path
,
os
.
O_CREAT
|
os
.
O_EXCL
)
except
IOError
as
e
:
logger
.
error
(
'
cannot create lock file %s for pid file %s
'
,
lock_path
,
pid_path
)
logger
.
error
(
'
remove the lock file manually and run again if you are confident no other instance is active
'
)
raise
pid_file
=
open
(
pid_path
,
'
w
'
)
pid_file
.
write
(
str
(
os
.
getpid
()))
pid_file
.
flush
()
os
.
close
(
lock_file
)
os
.
unlink
(
lock_path
)
try
:
yield
pid_file
finally
:
pid_file
.
close
()
# Assume that's atomic and all is well, no need for another .lock
os
.
unlink
(
pid_path
)
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment