diff --git a/bin/meson.build b/bin/meson.build index d9b667ccbd838c3a23c010fcd4b7a265ba0f66f7..8604b408549ae78432cd1add5e49d14255049c71 100644 --- a/bin/meson.build +++ b/bin/meson.build @@ -51,10 +51,21 @@ executable( install_rpath : bin_rpath, ) +executable( + 'steam-runtime-steam-remote', + 'steam-remote.c', + dependencies : [glib, gio_unix, libglnx_dep, libsteamrt_static_dep], + install : true, + # Use GLib from the adjacent libdir, ignoring LD_LIBRARY_PATH + build_rpath : bin_rpath, + install_rpath : bin_rpath, +) + if get_option('man') foreach bin_name : [ 'check-requirements', 'input-monitor', + 'steam-remote', 'system-info', ] custom_target( diff --git a/bin/steam-remote.c b/bin/steam-remote.c new file mode 100644 index 0000000000000000000000000000000000000000..83d8005305316fe7ebbad6839c5d3c54dc5f61f0 --- /dev/null +++ b/bin/steam-remote.c @@ -0,0 +1,60 @@ +/* + * Copyright © 2021 Collabora Ltd. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * Stub Steam executable that tries to directly pass the given commands to + * the running Steam client. + */ + +#include <libglnx.h> + +#include <glib.h> +#include <glib-object.h> + +#include <steam-runtime-tools/utils-internal.h> + +int +main (int argc, + const char **argv) +{ + static const char *default_argv[] = {"steam", "-foreground", NULL}; + g_autoptr(GError) error = NULL; + + if (argc <= 1) + { + /* If we don't have arguments, Steam is expected to send the command + * "-foreground" by default */ + argv = default_argv; + argc = 2; + } + + if (!_srt_steam_command_via_pipe (argv + 1, argc - 1, &error)) + { + g_printerr ("steam-runtime-steam-remote: %s\n", error->message); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/bin/steam-remote.md b/bin/steam-remote.md new file mode 100644 index 0000000000000000000000000000000000000000..75aeb265128ede992afdbcc874357ba9fc005d1f --- /dev/null +++ b/bin/steam-remote.md @@ -0,0 +1,55 @@ +--- +title: steam-runtime-steam-remote +section: 1 +... + +# NAME + +steam-runtime-steam-remote - stub Steam executable + +# SYNOPSIS + +**steam-runtime-steam-remote** [*ARGUMENT*]... + +# DESCRIPTION + +**steam-runtime-steam-remote** passes its command-line arguments +to the Steam client that is currently running, if any. + +The command-line arguments will typically be a single +[steam: URL](https://developer.valvesoftware.com/wiki/Steam_browser_protocol) +such as `steam://advertise/70`, but can include other +[Steam command-line options](https://developer.valvesoftware.com/wiki/Command_Line_Options#Steam_.28Windows.29) +such as `-foreground`. + +A typical use for **steam-runtime-steam-remote** is to add a symbolic +link named **steam** to a directory in the **PATH**, so that when a +game runs a command like `steam -foreground`, it will be converted into +an inter-process communication to the Steam client that is already running. + +# OUTPUT + +If the commands are correctly passed, the output will be empty. + +On error, a human-readable message is shown on standard error. + +# EXIT STATUS + +0 +: **steam-runtime-steam-remote** ran successfully + +Other Nonzero +: An error occurred. + +# EXAMPLES + +Bring Steam's graphical user interface to the foreground: + + steam-runtime-steam-remote -foreground + +Attempt to install Half-Life: + + steam-runtime-steam-remote steam://install/70 + + +<!-- vim:set sw=4 sts=4 et: --> diff --git a/debian/steam-runtime-tools-bin.install b/debian/steam-runtime-tools-bin.install index 47904f91da17e9501c4df494dee675980849a1a6..6a6434d18d43c22e0612150172a254dac2430d44 100644 --- a/debian/steam-runtime-tools-bin.install +++ b/debian/steam-runtime-tools-bin.install @@ -1,4 +1,5 @@ usr/bin/steam-runtime-check-requirements usr/bin/steam-runtime-input-monitor +usr/bin/steam-runtime-steam-remote usr/bin/steam-runtime-system-info usr/share/man/man1/steam-runtime-* diff --git a/steam-runtime-tools/utils-internal.h b/steam-runtime-tools/utils-internal.h index 6fdf671d7a8d15b51efbdfa214e55f38d1e45311..2234bea638c8e9ba3c29c02c7387c8440e02acab 100644 --- a/steam-runtime-tools/utils-internal.h +++ b/steam-runtime-tools/utils-internal.h @@ -109,6 +109,10 @@ G_GNUC_INTERNAL gboolean _srt_str_is_integer (const char *str); gboolean _srt_fstatat_is_same_file (int afd, const char *a, int bfd, const char *b); +G_GNUC_INTERNAL gboolean _srt_steam_command_via_pipe (const char * const *arguments, + gssize n_arguments, + GError **error); + /* * _srt_is_same_stat: * @a: a stat buffer diff --git a/steam-runtime-tools/utils.c b/steam-runtime-tools/utils.c index b9000c940072d376ab27e463cc162171b90dc5ff..7fe46472a23ed9c6370d8e4cc4d72783e5240867 100644 --- a/steam-runtime-tools/utils.c +++ b/steam-runtime-tools/utils.c @@ -1022,3 +1022,78 @@ _srt_fstatat_is_same_file (int afd, && fstatat (bfd, b, &b_buffer, AT_EMPTY_PATH) == 0 && _srt_is_same_stat (&a_buffer, &b_buffer)); } + +/* + * _srt_steam_command_via_pipe: + * @arguments: (not nullable) (element-type utf8) (transfer none): An array + * of command-line arguments that need to be passed to the Steam pipe + * @n_arguments: Number of elements of @arguments to use, or negative if + * @arguments is %NULL-terminated + * @error: Used to raise an error on failure + * + * If @n_arguments is positive it's the caller's responsibility to ensure that + * @arguments has at least @n_arguments elements. + * If @n_arguments is negative, @arguments must be %NULL-terminated. + * + * Returns: %TRUE if the provided @arguments were successfully passed to the + * running Steam client instance + */ +gboolean +_srt_steam_command_via_pipe (const char * const *arguments, + gssize n_arguments, + GError **error) +{ + glnx_autofd int fd = -1; + int ofd_flags; + g_autofree gchar *steampipe = NULL; + g_autoptr(GString) args_string = g_string_new (""); + gsize length; + gsize i; + + g_return_val_if_fail (arguments != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (n_arguments >= 0) + length = n_arguments; + else + length = g_strv_length ((gchar **) arguments); + + steampipe = g_build_filename (g_get_home_dir (), ".steam", "steam.pipe", NULL); + + fd = open (steampipe, O_WRONLY | O_NONBLOCK | O_CLOEXEC | O_NOCTTY); + + if (fd < 0 && (errno == ENOENT || errno == ENXIO)) + return glnx_throw_errno_prefix (error, "Steam is not running"); + else if (fd < 0) + return glnx_throw_errno_prefix (error, "An error occurred trying to open the Steam pipe"); + + ofd_flags = fcntl (fd, F_GETFL, 0); + + /* Remove O_NONBLOCK to block if we write more than the pipe-buffer space */ + if (fcntl (fd, F_SETFL, ofd_flags & ~O_NONBLOCK) != 0) + return glnx_throw_errno_prefix (error, "Unable to set flags on the steam pipe fd"); + + /* We hardcode the canonical steam installation path, instead of actually + * searching where Steam has been installed, because apparently this + * information is not used for anything in particular and Steam just + * discards it */ + g_string_append (args_string, "'~/.steam/root/ubuntu12_32/steam'"); + + for (i = 0; i < length; i++) + { + g_autofree gchar *quoted = NULL; + + quoted = g_shell_quote (arguments[i]); + + g_string_append (args_string, " "); + g_string_append (args_string, quoted); + } + + g_string_append (args_string, "\n"); + + if (glnx_loop_write (fd, args_string->str, args_string->len) < 0) + return glnx_throw_errno_prefix (error, + "An error occurred trying to write to the Steam pipe"); + + return TRUE; +}