77 Commits
Author SHA1 Message Date
Ikey Doherty 7d0acc9891 Add script to magically make a tarball, bump version to 0.4
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-11 00:32:29 +01:00
Ikey Doherty 7bae532d85 Update README with details about the intercept library
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-11 00:23:16 +01:00
Ikey Doherty 4f332c9acf Update mkgobuildy to install directly to the system
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-11 00:16:54 +01:00
Ikey Doherty f2dddd9f88 intercept: Catch the last of the SDL offenders like steamwebhelper
This now ensures steamwebhelper is also using our SDL as it was previously
loading from a relative location. This actually fixes issues on my local
system whereby the steam trailer videos couldn't full screen properly,
now using our own SDL, they can.

Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-11 00:15:52 +01:00
Ikey Doherty 94d5c6d939 intercept: Pretty print stuff
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 23:38:12 +01:00
Ikey Doherty f4564e2ab8 intercept: Be spammy only on demand, and provide relevant information
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 23:33:11 +01:00
Ikey Doherty 8d85cb3bed Fix storage of the use-libintercept setting
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 23:28:40 +01:00
Ikey Doherty fb2e84cb97 frontend: Teach UI how to control the libintercept setting
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 23:23:48 +01:00
Ikey Doherty e1f2783d11 shim: Allow disabling libintercept from config
Note libintercept requires that we use the native runtime, otherwise very
bad things will happen. ™

Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 23:18:11 +01:00
Ikey Doherty 13afbe69c3 lsi: Teach configuration about libintercept toggle
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 23:17:29 +01:00
Ikey Doherty b458346f5a Fix invalid date range for copyright assignment
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 23:12:53 +01:00
Ikey Doherty bf4fcfe533 Fix another trace of autotools
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 23:11:39 +01:00
Ikey Doherty b7ba890433 Start updating README for meson
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 23:10:47 +01:00
Ikey Doherty faa0c3a154 Nuke ancient TODO that knows not of which it speaks
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 23:06:32 +01:00
Ikey Doherty 99e54dd74b Drastically simplify gitignore..
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 23:05:58 +01:00
Ikey Doherty 5d18366a17 Nuke old autotools cruft
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 23:05:30 +01:00
Ikey Doherty 6154f2373c Allow installing .desktop files once more
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 23:03:51 +01:00
Ikey Doherty fe80861f66 frontend: Fix linking once more
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 23:00:12 +01:00
Ikey Doherty 38ec05ce98 Move lsi into new subdir to be reused as static lib
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 22:59:33 +01:00
Ikey Doherty d983f8ebdf Add currently failing build for frontend
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 22:54:51 +01:00
Ikey Doherty 4d3abc5e37 Enable GTK frontend, don't build Shim for 32-bit
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 22:52:45 +01:00
Ikey Doherty 47e50b789f shim: Only set LD_AUDIT if we were built with libintercept enabled
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 22:51:40 +01:00
Ikey Doherty 9905a2bc81 intercept: Allow controlling build of this library
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 22:50:28 +01:00
Ikey Doherty 11e1d8ae5f shim: Build lsi-steam or not at all, depending on meson options
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 22:46:19 +01:00
Ikey Doherty 00e78f705e Only build frontend and look for GTK if its requested
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 22:44:02 +01:00
Ikey Doherty 54c685bb1a Set things up to be configured via meson options
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 22:42:07 +01:00
Ikey Doherty a61e22d677 shim: When using native runtime, load liblsi-intercept in LD_AUDIT
liblsi-intercept has a simple job of stopping Steam from trying to
replace all of the system libraries in a mish-mash approach. Even when
we have STEAM_RUNTIME=0, and even with the advent of the new "prefer host
libraries" system, the internal replacements and pinning system actually
break massively.

To counteract this issue, we use our own rtld-audit implementation when
launching the real Steam, which will severely restrict which libraries the
main Steam binaries are actually allowed to load. This is on a whitelist
basis and ensures that the host libraries will be used where appropriate,
i.e. SDL, X11, etc. This prevents compatibility issues as well as the random
crashes we're now seeing with Steam beta.

Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 22:31:25 +01:00
Ikey Doherty 93b0f82279 Hack stuff up so we can build shim again
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 22:27:54 +01:00
Ikey Doherty d870577a55 intercept: Add missing modelines
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 22:07:15 +01:00
Ikey Doherty fd252e265d Move common helpers to common.h to fix compilation warnings
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 22:07:02 +01:00
Ikey Doherty 3684dfe11e intercept: More unused code
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 22:04:22 +01:00
Ikey Doherty d5b31dbf54 intercept: That guy is unused
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 22:03:43 +01:00
Ikey Doherty f6dcc30d33 shim: Ensure we disable GTK_MODULES (thanks @cybre!)
Same reason as XMODIFIERS - it'll stop Steam starting up properly

Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 21:56:11 +01:00
Ikey Doherty 7a2670f3e1 intercept: Completely clamp down on Steam, allow only Steam's own libs
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 21:54:59 +01:00
Ikey Doherty 61d9296a6c Let's pretend that one didnt happen
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 21:07:36 +01:00
Ikey Doherty f2bbe963b6 Clean up script so LD_AUDIT is a thing again
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 20:52:12 +01:00
Ikey Doherty 4547d8582a One more test to ensure this isnt environment related
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 20:50:50 +01:00
Ikey Doherty 1db376fa38 Turns out fatal is bad always because Steam is bad. Also, preload again
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 20:45:36 +01:00
Ikey Doherty 0bf704fc79 We like segfaults
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 20:44:08 +01:00
Ikey Doherty cf023ffb6a Ensure XMODIFIERS go bye bye
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 20:42:53 +01:00
Ikey Doherty 4b36347ade Remove unused define
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 20:41:51 +01:00
Ikey Doherty d3469e3eb0 intercept: Add quick hack which *should* work
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 20:37:19 +01:00
Ikey Doherty 29081c492d Make build script a bit more robust..
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 20:32:52 +01:00
Ikey Doherty ac76372b84 Add helper script so we can easily test liblsi-intercept
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 20:31:26 +01:00
Ikey Doherty 6b107f33b5 Ensure we can build everything now.
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 20:29:13 +01:00
Ikey Doherty ba8ae8f384 Add mkgobuildy to handle dual builds..
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 20:26:33 +01:00
Ikey Doherty d77ad58934 Remove intercept-emul32 because meson is weak a.f.
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 20:24:47 +01:00
Ikey Doherty 2aae934271 Ensure we can build 32-bit and 64-bit libraries together
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 20:16:11 +01:00
Ikey Doherty 12d5268252 Split emul32 into new dir
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 20:07:59 +01:00
Ikey Doherty 15253b97df Fail massively. But progress.
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 20:06:02 +01:00
Ikey Doherty 8dd27f6f24 intercept: Actually compile..
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 20:02:28 +01:00
Ikey Doherty f21559ee78 Make config.h work again
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 20:01:47 +01:00
Ikey Doherty 5b17fe9447 Start teaching intercept how to build (not complete.)
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 19:56:37 +01:00
Ikey Doherty 9ca7ffdd85 Get two nicas building, native and emul32
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 19:52:06 +01:00
Ikey Doherty 3b552ec17d Begin meson conversion.. Not yet building anything
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 19:46:00 +01:00
Ikey Doherty a3226a293c intercept: Build a static override table
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 19:15:21 +01:00
Ikey Doherty 9c40a9d106 intercept: Account for more processes
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 19:13:06 +01:00
Ikey Doherty 49d68c52a7 intercept: Teach intercept how to override libSDL2
This doesn't actually work properly yet as we need to build dual libraries
for 32-bit and 64-bit to ensure LD_AUDIT is non-fatal.

Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 19:09:30 +01:00
Ikey Doherty d20c230067 Begin basic libintercept design
This is the very start of our adventure into rtld-audit, and effectively
we'll have liblsi-intercept loaded by LD_AUDIT via our Steam LSI shim, and
when we find an interesting process, we'll match that process name and
activate ourselves.

In future, we'll want to blacklist Steam from being able to load duplicated
libraries on the system, i.e. libSDL, to resolve a number of crashes.

Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 18:17:14 +01:00
Ikey Doherty bd4707f3ca Fix nica submodule URI
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-10 14:53:26 +01:00
Ikey Doherty 1a70578cb1 shim: Work around Steam client's SDL regression with XMODIFIERS
The version of SDL shipping with Valve's Steam Client has a regression
that fails to correctly `SDL_InitSubSystem` whenever `XMODIFIERS` is set,
talking to dbus objects with no set address.

Relevant issue: ValveSoftware/steam-for-linux#501

This change will ensure that SDL can initialise correctly on startup,
and no longer make users suffer crashes.

Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2017-10-09 20:03:08 +01:00
Ikey Doherty 7995688ea2 Convert README to MD, and explain what LSI is, underp formatting
This resolves issue #6

Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2016-12-27 08:00:49 +00:00
Ikey Doherty e3360b7ddc Bump v3
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2016-12-09 00:16:46 +00:00
Ikey Doherty 1700f2616c Ensure we report that lsi-steam.desktop borrows translations
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2016-12-09 00:15:50 +00:00
Ikey Doherty 6f8e905569 Update README for --disable-replace-steam, remove excess in text
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2016-12-09 00:09:50 +00:00
Ikey Doherty 7c0ff5031e data: Incorporate actions from primary steam.desktop
Steam no longer writes a custom .desktop in into the local user directory,
so if the user is using a desktop that supports pinning a .desktop file,
it will be this one.

Thus, we enable the same actions, but going through the proxy of LSI.

Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2016-12-08 23:56:58 +00:00
Ikey Doherty dd5237e648 shim: Launch vanilla /usr/bin/steam when replacement is disabled
When we're not built for distro integration, we'll now call out to
/usr/bin/steam with the LSI modifications in place, so that we don't
enter into an infinite recursion.

Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2016-12-08 23:54:20 +00:00
Ikey Doherty 2282296045 Add an option to disable replacement of the Steam binary
LSI was initially created for Solus inclusion, and the best approach
determined was to replace /usr/bin/steam with our own stand in shim,
so that it was fully integrated.

Doing so enables one to run steam from anywhere and it would correctly
appear in the path, with no issues. However, this made LSI suitable
only for distribution integration, such as the very tight integration
seen in Solus.

With this change, the binary name will change to lsi-steam, and an
additional .desktop file to launch the shim will be installed. The
user must then use the LSI specific .desktop file to launch Steam,
at which point they'll be going through the LSI shim system and
reaping the benefits, without having to modify any packages.

More changes are yet to come, which will configure lsi-steam to call
the "real" steam in exec rather than the masked libdir variant.

Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2016-12-08 23:45:02 +00:00
Ikey Doherty ba48264207 update_format: Exclude nica from modification
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2016-12-08 22:28:07 +00:00
Ikey Doherty bddc0f29b1 Hook libnica into autotools and use this version now
TODO: Make coverage of libnica more intelligent so that we can drop
the extra conditional.

Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2016-12-08 22:16:20 +00:00
Ikey Doherty 196a039746 Include libnica as a git submodule
This ensures we only need to maintain the library in one place, greatly
reducing the complexity of integration and updating of the library.

Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2016-12-08 22:13:33 +00:00
Ikey Doherty 4ff98e225a Remove our local forked copy of libnica
This will pave the way for embedding libnica as a submodule and building
against the static library, removing any requirements for having the shared
library present.

As it seems nica fits better currently as a utility library, that's not
such a bad thing.

Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2016-12-08 22:12:11 +00:00
Ikey Doherty e49a7873fe Preload libxcb now with latest Steam runtime
It explicitly checks for xcb_poll_for_reply64, which must be preloaded before
libx11 to ensure it's properly resolved.

Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2016-10-12 04:40:31 +01:00
Ikey Doherty 6659905102 Merge pull request #4 from philipzae/patch-1
correct link to solus website
2016-07-25 22:03:38 +01:00
Yousuf 'Jay' Philips 16702d0d8f correct link to solus website 2016-07-25 23:44:14 +04:00
Ikey Doherty 00d8831720 nica: Sync with upstream commit a8b1f1ee423f386d1ff676d4173ebd83a64656de
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2016-06-15 13:01:23 +01:00
Ikey Doherty 451ffe7680 nica: Sync with upstream
Signed-off-by: Ikey Doherty <ikey@solus-project.com>
2016-06-11 18:47:54 +01:00
34 changed files with 806 additions and 1623 deletions
+2 -18
View File
@@ -1,18 +1,2 @@
Makefile
Makefile.in
aclocal.m4
autom4te.cache/
build-aux/
config.*
configure
libtool
stamp-h1
m4/*.m4
.deps
.dirstamp
*.o
*.lo
*.la
.libs
/steam
/lsi-settings
/buildroot/
/build/
+3
View File
@@ -0,0 +1,3 @@
[submodule "src/libnica"]
path = src/libnica
url = git@github.com:intel/libnica.git
-82
View File
@@ -1,82 +0,0 @@
EXTRA_DIST = \
LICENSE.LGPL2.1 \
README.rst \
data/lsi-settings.desktop
AM_CFLAGS = \
-fstack-protector -Wall -pedantic \
-Wstrict-prototypes -Wundef -fno-common \
-Werror-implicit-function-declaration \
-Wformat -Wformat-security -Werror=format-security \
-Wno-conversion -Wunused-variable -Wunreachable-code \
-Wall -W -D_FORTIFY_SOURCE=2 -std=c99
AM_CPPFLAGS = \
-I $(top_srcdir)/src \
-I $(top_srcdir)/src/shim \
-I $(top_srcdir)/src/nica \
-DSYSTEMCONFDIR=\"$(sysconfdir)\" \
-DVENDORDIR=\"$(datadir)/defaults/linux-steam-integration\"
noinst_LTLIBRARIES = \
libnica.la \
liblsi.la
bin_PROGRAMS = \
steam
if HAVE_FRONTEND
bin_PROGRAMS += lsi-settings
lsi_settings_SOURCES = \
src/frontend/main.c
lsi_settings_CFLAGS = \
$(GTK_CFLAGS) \
$(AM_CFLAGS) \
-DGDK_VERSION_MAX_ALLOWED=GDK_VERSION_3_4 \
-DGDK_VERSION_MIN_REQUIRED=GDK_VERSION_3_4
lsi_settings_LDADD = \
liblsi.la \
$(GTK_LIBS)
desktopdir = $(datadir)/applications
desktop_DATA = \
data/lsi-settings.desktop
endif
# Convenience library to prevent external linking with currently unstable ABI
libnica_la_SOURCES = \
src/nica/hashmap.h \
src/nica/hashmap.c \
src/nica/inifile.h \
src/nica/inifile.c \
src/nica/util.h
libnica_la_CFLAGS = \
$(AM_CFLAGS)
# Convenience library for shared LSI functions
liblsi_la_SOURCES = \
src/shim/lsi.h \
src/shim/lsi.c
liblsi_la_CFLAGS = \
$(AM_CFLAGS)
liblsi_la_LIBADD = \
libnica.la
steam_SOURCES = \
src/shim/shim.c
steam_LDADD = \
libnica.la \
liblsi.la
steam_CFLAGS = \
$(AM_CFLAGS)
+161
View File
@@ -0,0 +1,161 @@
linux-steam-integration
-----------------------
Linux Steam Integration attempts to improve the Steam* gaming experience for users on Linux. In the default mode, it will disable the Steam runtime, and facilitate usage of the distro's own runtime for improved integration and performance. The Steam Runtime is a very old set of Ubuntu libraries, and as such provides a suboptimial experience for many Linux users. A key example of this is broken fontconfig, C++ ABI issues preventing loading, or even the broken theming of GTK widgets on non-Ubuntu distros. These woes have been compounded for users of the open source drivers.
The LSI tool also allows you to use Steam's runtime if you need to, as currently not all cases are covered. It will also take care to prepare the environment, ensuring that users can load Steam using it's own runtime without hitting any of the issues common to a modern distro, i.e. ABI issues and having to `LD_PRELOAD` libraries to even make Steam start.
More importantly, LSI achieves all of this by not having to **butcher your existing Steam installation**, as is common in some new tools.
This project, and by extension Solus, is not officially endorsed by, or affiliated with, Steam*, or its parent company, Valve*.
Linux Steam Integration is a [Solus project](https://solus-project.com/)
![logo](https://build.solus-project.com/logo.png)
Integrating LSI
===============
To correctly integrate LSI, your Steam package will require modification. LSI must provide the /usr/bin/steam binary, so your Steam package must move the main launcher to a shadow location.
For users who do not have access to LSI in their distribution, because it has not been integrated yet, please configure with `-Dwith-shim=co-exist` to preserve your existing Steam integrity. Then use the "LSI Steam" shortcut in your menu to launch Steam via LSI.
**Configuring LSI build**
There are a number of meson configure options you should be aware of when integrating LSI correctly. The prominent ones are explained here.
`-Dwith-shim= none | co-exist | replacement`
`--disable-replace-steam`
Control behaviour of the shim build. `none` will disable building the main
shim, required for actually launching Steam in the first place.
`co-exist` will ensure this does not conflict with the `/usr/bin/steam`
path, providing a new `lsi-steam` binary and desktop file.
`replacement` will provide a drop-in replacement `/usr/bin/steam` binary
shim responsible for bootstrapping Steam via LSI. This will require you
to mask the original steam binary to a new location.
The default value for this option is::
replacement
`-Dwith-steam-binary=$PATH`
Set the absolute path for the shadowed Steam binary.
LSI will execv Steam after it has initialised the environment.
Note that execv, not execvp, is used, to mask programs that may be
in the `$PATH`, hence requiring an absolute path.
The default value for this option is::
/usr/lib/steam/steam
This option is only applicable for distribution integrators, when
Steam replacement is enabled.
`-Dwith-preload-libs=$LIBS`
A colon separated list of libraries that are required to launch Steam
when using its own runtime. LSI enables users to switch back to the Steam
runtime, and in this instance we manage the LD_PRELOAD environmental variable.
Ensure this is correct, and escape $LIB for correct Linux usage.
The default value for this option is::
/usr/\$LIB/libX11.so.6:/usr/\$LIB/libstdc++.so.6
`-Dwith-frontend=$boolean`
A small UI application is shipped to enable configuration of LSI, which presents
a simple GTK3 Dialog to the user. It is not enabled by default, it is up
to the integrator to decide if they wish to employ lsi-settings, or implement
an alternative.
The lsi-settings application will only ever write new configurations to the
**user** settings file, and requires no special permissions.
The default value for this option is::
false
**How LSI Works**
LSI provides a /usr/bin/steam binary to be used in place of the existing Steam script, which will then correctly set up the environment before swapping the process for the Steam process.
When using a full build of LSI, the bootstrap shim will force a correct environment, removing any issues that can cause various older SDL versions to kill Steam. Additionally it will then
force `STEAM_RUNTIME=0` if this is set in the config (default behaviour).
When using the native runtime, and the `intercept` library is enabled, the LSI shim will also set up `LD_AUDIT` to use `liblsi-intercept`. This library will intercept all dynamic linking operations
for the main Steam binaries and override their behaviour, allowing Steam to only load a handful of vendored libraries (`libav` fork) as well as its own private libraries. It is then forced to use
system libraries for everything else. This means the Steam client, web helper, etc, will use the native SDL, X11, fixing many bugs with video playback, font rendering and such.
If you are packaging LSI for a distribution, please ensure you provide both a 32-bit and 64-bit build of the intercept library so that the entirety of Steam's library (`.so`) mechanism is tightly
controlled by LSI. See the scripts in the root directory of this repository for examples of how to do this.
Configuration options can be placed in an INI-style configuration file in a series of locations, which are ordered by priority in a cascade::
~/.config/linux-steam-integration.conf
/etc/linux-steam-integration.conf
/usr/share/defaults/linux-steam-integration/linux-steam-integration.conf
The user configuration takes immediate priority. Secondly we have the system-wide configuration for affecting all accounts, and lastly the vendor configuration, which may be provided by the integrator.
Currently this INI file supports two options. The root section in this INI file must be `[Steam]`.
`use-native-runtime = $boolean`
If set to a true boolean value, (yes/true/on), the host OS's native runtime
will be used instead of the Steam provided runtime. If this is set to
a false boolean value (no/false/off), then the startup will be modified
to export the relevant `LD_PRELOAD` and `STEAM_RUNTIME` settings.
The default value of this variable is true.
`force-32bit = $boolean`
If set to a true boolean value (yes/true/on), the shadowed Steam binary will
be run via the `linux32` command. This will force the `steam` process
and all children to believe they are running on a 32-bit system. This
may be useful for 64-bit games that are buggy only on 64-bit.
If this is set to a false boolean value (no/false/off), then the
shadowed Steam binary will be directly executed. Note that on 32-bit
OSs this setting is ignored.
The default value of this variable is false.
Common issues
=============
**Missing tray icon for Steam client using native OS runtime**
Ensure you have the 32-bit version of libappindicator installed. This is required for the tray icon. libappindicator falls back to a standard X11 tray icon in the absence of desktop appindicator support.
Related issue: [Steam tray icon missing #2](https://github.com/solus-project/linux-steam-integration/issues/2)
License
-------
`src/shim src/frontend src/lsi src/intercept`:
Copyright © 2016-2017 Ikey Doherty
linux-steam-integration is available under the terms of the `LGPL-2.1`
`data/lsi-steam.desktop`:
This file borrows translations from the official `steam.desktop` launcher.
These are copyright of Valve*.
See `LICENSE.LGPL2.1 <LICENSE.LGPL2.1>`_ for more details
* Some names may be claimed as the property of others.
-148
View File
@@ -1,148 +0,0 @@
linux-steam-integration
-----------------------
A helper shim to enable better Steam* integration on Linux systems.
This is part of an effort by Solus to enhance Steam for everyone.
This project, and by extension Solus, is not officially endorsed by, or
affiliated with, Steam*, or its parent company, Valve*.
Linux Steam Integration (LSI) is a `Solus Project <https://github.com/solus-project/linux-steam-integration/issues/2>`_
.. image:: https://build.solus-project.com/logo.png
:align: center
:alt: LSI is a Solus Project
Integrating LSI
===============
To correctly integrate LSI, your Steam package will require modification.
LSI must provide the /usr/bin/steam binary, so your Steam package must
move the main launcher to a shadow location.
**Configuring LSI build**
There are a number of configure options you should be aware of when integrating
LSI correctly. The prominent ones are explained here.
``--with-real-steam-binary=$NAME``
Set the absolute path for the shadowed Steam binary.
LSI will execv Steam after it has initialised the environment.
Note that execv, not execvp, is used, to mask programs that may be
in the ``$PATH``, hence requiring an absolute path.
The default value for this option is::
/usr/lib/steam/steam
``--with-preload-libs=$LIBS``
A colon separated list of libraries that are required to launch Steam
when using its own runtime. LSI enables users to switch back to the Steam
runtime, and in this instance we manage the LD_PRELOAD environmental variable.
Ensure this is correct, and escape $LIB for correct Linux usage.
The default value for this option is::
/usr/\$LIB/libX11.so.6:/usr/\$LIB/libstdc++.so.6
``--enable-frontend``
A small UI application is shipped to enable configuration of LSI, which presents
a simple GTK3 Dialog to the user. It is not enabled by default, it is up
to the integrator to decide if they wish to employ lsi-settings, or implement
an alternative.
The lsi-settings application will only ever write new configurations to the
**user** settings file, and requires no special permissions.
The default value for this option is::
disabled
**How LSI Works**
LSI provides a /usr/bin/steam binary to be used in place of the existing Steam script,
which will then correctly set up the environment before swapping the process for the
Steam process.
Configuration options can be placed in an INI-style configuration file in a series
of locations, which are ordered by priority in a cascade::
~/.config/linux-steam-integration.conf
/etc/linux-steam-integration.conf
/usr/share/defaults/linux-steam-integration/linux-steam-integration.conf
The user configuration takes immediate priority. Secondly we have the system-wide
configuration for affecting all accounts, and lastly the vendor configuration,
which may be provided by the integrator.
Currently this INI file supports two options. The root section in this INI file
must be ``[Steam]``.
``use-native-runtime = $boolean``
If set to a true boolean value, (yes/true/on), the host OS's native runtime
will be used instead of the Steam provided runtime. If this is set to
a false boolean value (no/false/off), then the startup will be modified
to export the relevant ``LD_PRELOAD`` and ``STEAM_RUNTIME`` settings.
The default value of this variable is ``true``.
``force-32bit = $boolean``
If set to a true boolean value (yes/true/on), the shadowed Steam binary will
be run via the ``linux32`` command. This will force the ``steam`` process
and all children to believe they are running on a 32-bit system. This
may be useful for 64-bit games that are buggy only on 64-bit.
If this is set to a false boolean value (no/false/off), then the
shadowed Steam binary will be directly executed. Note that on 32-bit
OSs this setting is ignored.
The default value of this variable is ``false``.
Common issues
=============
**Missing tray icon for Steam client using native OS runtime**
Ensure you have the 32-bit version of libappindicator installed. This
is required for the tray icon. libappindicator falls back to a standard
X11 tray icon in the absence of desktop appindicator support.
Related issue: `Steam tray icon missing #2 <https://github.com/solus-project/linux-steam-integration/issues/2>`_
License
-------
``src/nica``::
Partial import of libnica, Copyright © Intel Corporation.
Used within the shim component to ensure it is leak free
and as lightweight as possible, due to needing to exec the
main Steam* launcher.
libnica is available under the terms of the `LGPL-2.1` license.
``src/shim src/frontend``::
Copyright © 2016 Ikey Doherty
linux-steam-integration is available under the terms of the `LGPL-2.1`
See `LICENSE.LGPL2.1 <LICENSE.LGPL2.1>`_ for more details
* Some names may be claimed as the property of others.
-20
View File
@@ -1,20 +0,0 @@
LSI is still in its infancy, currently only at a 0.1 release. The time to
release was dramatic due to an absolute requirement for non-broken Steam
to accompany the Solus 1.2 release
Going forward, there are more items that need implementing to start shaving
more rough edges off of Linux gaming.
Immediate LSI worklist:
- [x] Add configurable shim to replace /usr/bin/steam, relying on shadowed launcher
- [x] Add super-basic UI to configure said shim
- [x] Shove out a 0.1 for Solus 1.2
- [ ] Replace existing /usr/bin/steam entirely and decouple the tight runtime logic
that is currently enforced.
- [ ] Remove requirement for steam.sh $bindir/steam entirely - allowing parallel clients.
LSI becomes more self contained and no longer needs to prop up Steam, but replaces
much of the support tools.
- [ ] Replace the existing Steam "run.sh" style launch facilities
- [ ] Get to a point that current ubuntu12_32 runtime is no longer required or
wanted.
-14
View File
@@ -1,14 +0,0 @@
#!/bin/sh
set -e
autoreconf --force --install --symlink --warnings=all
args="\
--sysconfdir=/etc \
--localstatedir=/var \
--prefix=/usr \
--enable-silent-rules"
./configure CFLAGS="-g -O1 $CFLAGS" $args "$@"
make clean
-103
View File
@@ -1,103 +0,0 @@
AC_INIT([linux-steam-integration], 0.2, [ikey@solus-project.com], [linux-steam-integration], [https://github.com/solus-project/linux-steam-integration/issues])
AC_CONFIG_AUX_DIR([build-aux])
AM_INIT_AUTOMAKE([-Wno-portability no-dist-gzip dist-xz foreign subdir-objects])
AC_PROG_CC
AC_PROG_CXX
AC_PROG_CC_STDC
LT_PREREQ(2.2)
AC_CONFIG_HEADERS([config.h])
AC_PREFIX_DEFAULT(/usr/local)
AM_SILENT_RULES([yes])
LT_INIT([disable-static])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_FILES([Makefile])
# Check if we're using the new C++ ABI, which will require LD_PRELOAD magic
AC_LANG_PUSH([C++])
AC_MSG_CHECKING(['_GLIBCXX_USE_CXX11_ABI' value])
AC_COMPUTE_INT([LSI_NEW_CXX_ABI], [_GLIBCXX_USE_CXX11_ABI], [[#include <string>]], [LSI_NEW_CXX_ABI=0])
AC_MSG_RESULT([$LSI_NEW_CXX_ABI])
AC_LANG_POP([C++])
if test x$LSI_NEW_CXX_ABI = "x1"; then
use_new_abi="yes"
AC_DEFINE([LSI_USE_NEW_ABI], [1], [Using new C++ ABI])
else
use_new_abi="no"
fi
# Source: https://mail.gnome.org/archives/commits-list/2012-September/msg01730.html
# adl_RECURSIVE_EVAL(VALUE, RESULT)
# =================================
# Interpolate the VALUE in loop until it doesn't change,
# and set the result to $RESULT.
# WARNING: It's easy to get an infinite loop with some unsane input.
# For example ${datadir} becomes ${datarootdir}, and then ${prefix}/share, and
# finally ${prefix} is replaced by the prefix.
AC_DEFUN([adl_RECURSIVE_EVAL],
[_lcl_receval="$1"
$2=`(test "x$prefix" = xNONE && prefix="$ac_default_prefix"
test "x$exec_prefix" = xNONE && exec_prefix="${prefix}"
_lcl_receval_old=''
while test "[$]_lcl_receval_old" != "[$]_lcl_receval"; do
_lcl_receval_old="[$]_lcl_receval"
eval _lcl_receval="\"[$]_lcl_receval\""
done
echo "[$]_lcl_receval")`])
# Shadow location of real Steam
AC_ARG_WITH([real-steam-binary], AS_HELP_STRING([--with-real-steam-binary],
[the real Steam binary to use]), [steambin=${withval}],
[steambin="$libdir/steam/steam"])
adl_RECURSIVE_EVAL(["$steambin"], [STEAMBIN])
AC_SUBST(STEAMBIN)
AC_DEFINE_UNQUOTED(STEAM_BINARY, "$STEAMBIN",
[The location of the real Steam binary])
# The LD_PRELOAD list required to integrate gracefully with the official Steam Runtime
AC_ARG_WITH([preload-libs], AS_HELP_STRING([--with-preload-libs],
[the preload libs to use with Steams own runtime]), [preloadlibs="${withval}"],
[preloadlibs="/usr/\$LIB/libX11.so.6:/usr/\$LIB/libstdc++.so.6"])
AC_DEFINE_UNQUOTED([LSI_PRELOAD_LIBS], "$preloadlibs",
[Libraries to preload with Steams runtime])
m4_define([gtk_required_version], [3.4.0])
# Check if frontend is requested
have_frontend="no"
AC_ARG_ENABLE(frontend, AS_HELP_STRING([--enable-frontend], [enable GTK frontend]))
if test "x$enable_frontend" = "xyes" ; then
PKG_CHECK_MODULES([GTK], [gtk+-3.0 >= gtk_required_version])
AC_DEFINE([HAVE_FRONTEND], [1], [Enabled GTK+ frontend])
have_frontend="yes"
fi
AM_CONDITIONAL([HAVE_FRONTEND], [test x$have_frontend = "xyes"])
AC_OUTPUT
AC_MSG_RESULT([
linux-steam-integration $VERSION
========
prefix: ${prefix}
libdir: ${libdir}
sysconfdir: ${sysconfdir}
exec_prefix: ${exec_prefix}
bindir: ${bindir}
datarootdir: ${datarootdir}
New C++ ABI: ${use_new_abi}
Real Steam binary: ${STEAMBIN}
GTK+ Frontend: ${have_frontend}
preload-libs: ${preloadlibs}
compiler: ${CC}
cflags: ${CFLAGS}
ldflags: ${LDFLAGS}
])
+107
View File
@@ -0,0 +1,107 @@
[Desktop Entry]
Name=LSI Steam
Comment=Launch Steam via LSI
Exec=/usr/bin/lsi-steam %U
Icon=steam
Terminal=false
Type=Application
Categories=Network;FileTransfer;Game;
MimeType=x-scheme-handler/steam;
Actions=Store;Community;Library;Servers;Screenshots;News;Settings;BigPicture;Friends;
[Desktop Action Store]
Name=Store
Name[de]=Shop
Name[es]=Tienda
Name[fr]=Magasin
Name[it]=Negozio
Name[pt]=Loja
Name[ru]=Магазин
Name[zh_CN]=商店
Name[zh_TW]=商店
Exec=lsi-steam steam://store
[Desktop Action Community]
Name=Community
Name[es]=Comunidad
Name[fr]=Communauté
Name[it]=Comunità
Name[pt]=Comunidade
Name[ru]=Сообщество
Name[zh_CN]=社区
Name[zh_TW]=社群
Exec=lsi-steam steam://url/SteamIDControlPage
[Desktop Action Library]
Name=Library
Name[de]=Bibliothek
Name[es]=Biblioteca
Name[fr]=Bibliothèque
Name[it]=Libreria
Name[pt]=Biblioteca
Name[ru]=Библиотека
Name[zh_CN]=
Name[zh_TW]=遊戲庫
Exec=lsi-steam steam://open/games
[Desktop Action Servers]
Name=Servers
Name[de]=Server
Name[es]=Servidores
Name[fr]=Serveurs
Name[it]=Server
Name[pt]=Servidores
Name[ru]=Серверы
Name[zh_CN]=服务器
Name[zh_TW]=伺服器
Exec=lsi-steam steam://open/servers
[Desktop Action Screenshots]
Name=Screenshots
Name[es]=Capturas
Name[fr]=Captures d'écran
Name[it]=Screenshot
Name[ru]=Скриншоты
Name[zh_CN]=截图
Name[zh_TW]=螢幕擷圖
Exec=lsi-steam steam://open/screenshots
[Desktop Action News]
Name=News
Name[de]=Neuigkeiten
Name[es]=Noticias
Name[fr]=Actualités
Name[it]=Notizie
Name[pt]=Notícias
Name[ru]=Новости
Name[zh_CN]=新闻
Name[zh_TW]=新聞
Exec=lsi-steam steam://open/news
[Desktop Action Settings]
Name=Settings
Name[de]=Einstellungen
Name[es]=Parámetros
Name[fr]=Paramètres
Name[it]=Impostazioni
Name[pt]=Configurações
Name[ru]=Настройки
Name[zh_CN]=设置
Name[zh_TW]=設定
Exec=lsi-steam steam://open/settings
[Desktop Action BigPicture]
Name=Big Picture
Exec=lsi-steam steam://open/bigpicture
[Desktop Action Friends]
Name=Friends
Name[de]=Freunde
Name[es]=Amigos
Name[fr]=Amis
Name[it]=Amici
Name[pt]=Amigos
Name[ru]=Друзья
Name[zh_CN]=好友
Name[zh_TW]=好友
Exec=lsi-steam steam://open/friends
+17
View File
@@ -0,0 +1,17 @@
appsdir = join_paths(datadir, 'applications')
# If frontend is enabled, install lsi-settings.desktop
if with_frontend == true
install_data(
'lsi-settings.desktop',
install_dir: appsdir,
)
endif
# If we need to co-exist, install alternative .desktop file (not conflicting with Steam)
if shim_behaviour == 'co-exist'
install_data(
'lsi-steam.desktop',
install_dir: appsdir,
)
endif
-1
View File
@@ -1 +0,0 @@
+70
View File
@@ -0,0 +1,70 @@
project(
'linux-steam-integration',
['c'],
version: '0.4',
license: [
'LGPL-2.1',
],
default_options: [
'c_std=c99',
],
)
am_cflags = [
'-fstack-protector',
'-pedantic',
'-Wstrict-prototypes',
'-Wundef',
'-fno-common',
'-Werror-implicit-function-declaration',
'-Wformat',
'-Wformat-security',
'-Werror=format-security',
'-Wno-conversion',
'-Wunused-variable',
'-Wunreachable-code',
'-Wall',
'-W',
'-D_FORTIFY_SOURCE=2',
]
add_global_arguments(am_cflags, language: 'c')
dep_gtk3 = dependency('gtk+-3.0', version: '>= 3.4.0')
cdata = configuration_data()
cdata.set_quoted('PACKAGE_NAME', meson.project_name())
prefix = get_option('prefix')
datadir = join_paths(prefix, get_option('datadir'))
vendordir = join_paths(datadir, 'defaults', meson.project_name())
sysconfdir = join_paths(prefix, get_option('sysconfdir'))
cdata.set_quoted('SYSTEMCONFDIR', sysconfdir)
cdata.set_quoted('VENDORDIR', vendordir)
# Configurables
cdata.set_quoted('LSI_PRELOAD_LIBS', get_option('with-preload-libs'))
cdata.set_quoted('STEAM_BINARY', get_option('with-steam-binary'))
shim_behaviour = get_option('with-shim')
if shim_behaviour == 'replacement'
cdata.set('REPLACE_STEAM', '1')
endif
with_libintercept = get_option('with-libintercept')
if with_libintercept == true
cdata.set('HAVE_LIBINTERCEPT', '1')
endif
with_frontend = get_option('with-frontend')
config_h = configure_file(
configuration: cdata,
output: 'config.h',
)
config_h_dir = include_directories('.')
subdir('data')
subdir('src')
+7
View File
@@ -0,0 +1,7 @@
option('with-shim', type : 'combo', choices : ['replacement', 'co-exist', 'none'], default: 'replacement',
description : 'control how the shim is deployed')
option('with-steam-binary', type: 'string', description: 'Set the path to the Steam binary', value: '/usr/lib/steam/steam')
option('with-frontend', type: 'boolean', description: 'Build the LSI Settings UI', value: false)
option('with-preload-libs', type: 'string', description: 'Colon separated list of libraries required to launch Steam games',
value: '/usr/\$LIB/libxcb.so.1:/usr/\$LIB/libX11.so.6:/usr/\$LIB/libstdc++.so.6')
option('with-libintercept', type: 'boolean', description: 'Enable libintercept library for controlling Steam linking', value: true)
Executable
+18
View File
@@ -0,0 +1,18 @@
#!/bin/bash
set -e
if [[ ! -d buildroot/build32 ]]; then
CC="gcc -m32" linux32 meson buildroot/build32 --prefix=/usr --sysconfdir=/etc --datadir=/usr/share --libdir=/usr/lib32 -Dwith-shim=none
fi
if [[ ! -d buildroot/build64 ]]; then
meson buildroot/build64 --prefix=/usr --sysconfdir=/etc --datadir=/usr/share --libdir=/usr/lib64 -Dwith-shim=replacement -Dwith-steam-binary=/usr/lib64/steam/steam -Dwith-frontend=true
fi
ninja -C buildroot/build32
ninja -C buildroot/build64
sudo ninja -C buildroot/build32 install
sudo ninja -C buildroot/build64 install
Executable
+14
View File
@@ -0,0 +1,14 @@
#!/bin/bash
set -e
git submodule init
git submodule update
# Script for ikey because he went with meson. *shrug*
VERSION="0.4"
NAME="linux-steam-integration"
git-archive-all.sh --format tar --prefix ${NAME}-${VERSION}/ --verbose -t HEAD ${NAME}-${VERSION}.tar
xz -9 "${NAME}-${VERSION}.tar"
gpg --armor --detach-sign "${NAME}-${VERSION}.tar.xz"
gpg --verify "${NAME}-${VERSION}.tar.xz.asc"
+35
View File
@@ -0,0 +1,35 @@
/*
* This file is part of linux-steam-integration.
*
* Copyright © 2016-2017 Ikey Doherty
*
* linux-steam-integration is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*/
#pragma once
#define _GNU_SOURCE
/* Mark a variable/argument as unused to skip warnings */
#define __lsi_unused__ __attribute__((unused))
/* Force inlining of a function */
#define __lsi_inline__ __attribute__((always_inline))
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 8
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* vi: set shiftwidth=8 tabstop=8 expandtab:
* :indentSize=8:tabSize=8:noTabs=true:
*/
+23 -1
View File
@@ -1,7 +1,7 @@
/*
* This file is part of linux-steam-integration.
*
* Copyright © 2016 Ikey Doherty
* Copyright © 2016-2017 Ikey Doherty <ikey@solus-project.com>
*
* linux-steam-integration is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License as
@@ -17,6 +17,7 @@
#include <string.h>
#include <sys/stat.h>
#include "config.h"
#include "lsi.h"
static inline void _align_label(GtkWidget *label)
@@ -40,6 +41,9 @@ int main(int argc, char **argv)
GtkWidget *image = NULL;
GtkWidget *check_native = NULL;
GtkWidget *check_emul32 = NULL;
#ifdef HAVE_LIBINTERCEPT
GtkWidget *check_intercept = NULL;
#endif
GtkWidget *label = NULL;
GtkSettings *settings = NULL;
int response = 0;
@@ -110,6 +114,17 @@ int main(int argc, char **argv)
gtk_widget_set_sensitive(label, is_x86_64);
gtk_widget_set_sensitive(check_emul32, is_x86_64);
#ifdef HAVE_LIBINTERCEPT
label = gtk_label_new("Use liblsi-intercept to override Steam's library loading mechanism");
_align_label(label);
gtk_widget_set_tooltip_text(label,
"This can help with many library issues with the core client");
gtk_grid_attach(GTK_GRID(grid), label, 1, 3, 1, 1);
check_intercept = gtk_switch_new();
gtk_grid_attach(GTK_GRID(grid), check_intercept, 2, 3, 1, 1);
#endif
gtk_container_set_border_width(GTK_CONTAINER(grid), 6);
gtk_widget_set_margin_bottom(grid, 18);
@@ -122,6 +137,10 @@ int main(int argc, char **argv)
gtk_switch_set_active(GTK_SWITCH(check_native), lconfig.use_native_runtime);
gtk_switch_set_active(GTK_SWITCH(check_emul32), lconfig.force_32);
#ifdef HAVE_LIBINTERCEPT
gtk_switch_set_active(GTK_SWITCH(check_intercept), lconfig.use_libintercept);
#endif
/* Run the dialog */
gtk_widget_show_all(container);
response = gtk_dialog_run(GTK_DIALOG(dialog));
@@ -134,6 +153,9 @@ int main(int argc, char **argv)
/* Get the config from the UI */
lconfig.use_native_runtime = gtk_switch_get_active(GTK_SWITCH(check_native));
lconfig.force_32 = gtk_switch_get_active(GTK_SWITCH(check_emul32));
#ifdef HAVE_LIBINTERCEPT
lconfig.use_libintercept = gtk_switch_get_active(GTK_SWITCH(check_intercept));
#endif
/* Cleanup */
gtk_widget_destroy(dialog);
+18
View File
@@ -0,0 +1,18 @@
if with_frontend == true
dep_gtk3 = dependency('gtk+-3.0', version: '>= 3.4.0')
frontend_sources = [
'main.c',
]
frontend = executable(
'lsi-settings',
sources: frontend_sources + nica_sources,
include_directories: nica_includes,
dependencies: [
dep_gtk3,
link_lsi,
],
install: true,
)
endif
+192
View File
@@ -0,0 +1,192 @@
/*
* This file is part of linux-steam-integration.
*
* Copyright © 2016-2017 Ikey Doherty <ikey@solus-project.com>
*
* linux-steam-integration is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*/
#define _GNU_SOURCE
#include <link.h>
#include <stdio.h>
#include <stdlib.h>
#include "../common.h"
#include "nica/nica.h"
/**
* Is this a process we're actually interested in?
*/
static bool process_supported = false;
static const char *matched_process = NULL;
/**
* Determine the basename'd process
*/
static inline char *get_process_name(void)
{
autofree(char) *realp = NULL;
char *basep = NULL;
realp = realpath("/proc/self/exe", NULL);
if (!realp) {
return false;
}
basep = basename(realp);
return strdup(basep);
}
/**
* Find out if we're being executed by a process we actually need to override,
* otherwise we'd not be loaded by rtld-audit
*/
static bool is_intercept_candidate(void)
{
static const char *wanted_processes[] = {
"html5app_steam",
"opengl-program",
"steam",
"steamwebhelper",
};
autofree(char) *nom = NULL;
nom = get_process_name();
if (!nom) {
return false;
}
for (size_t i = 0; i < ARRAY_SIZE(wanted_processes); i++) {
if (streq(wanted_processes[i], nom)) {
matched_process = wanted_processes[i];
if (!getenv("LSI_DEBUG")) {
return true;
}
fprintf(stderr,
"\033[32;1m[LSI:%s]\033[0m: debug: loading libintercept from LSI\n",
matched_process);
return true;
}
}
return false;
}
/**
* la_version is our main entry point and will only continue if our
* process is explicitly Steam
*/
_nica_public_ unsigned int la_version(unsigned int supported_version)
{
/* Unfortunately glibc will die if we tell it to skip us .. */
process_supported = is_intercept_candidate();
return supported_version;
}
/**
* emit_overriden lib is used to pretty-print the debug when we blacklist
* a vendored library
*/
static void emit_overriden_lib(const char *lib_name)
{
fprintf(stderr,
"\033[32;1m[LSI:%s]\033[0m: debug: blacklisted loading of vendored library: "
"\033[34;1m%s\033[0m\n",
matched_process,
lib_name);
}
/**
* la_objsearch will allow us to blacklist certain LD_LIBRARY_PATH duplicate
* libraries being loaded by the Steam client, such as the broken libSDL shipped
* as a private vendored lib
*/
_nica_public_ char *la_objsearch(const char *name, __lsi_unused__ uintptr_t *cookie,
__lsi_unused__ unsigned int flag)
{
/* We don't know about this process, so have glibc do its thing as normal */
if (!process_supported) {
return (char *)name;
}
// Patterns we'll permit Steam to privately load
static const char *steam_allowed[] = {
/* general */
"libicui18n.so",
"libicuuc.so",
"libavcodec.so.",
"libavformat.so.",
"libavresample.so.",
"libavutil.so.",
"libswscale.so.",
/* core plugins */
"chromehtml.so",
"crashhandler.so",
"filesystem_stdio.so",
"friendsui.so",
"gameoverlayrenderer.so",
"gameoverlayui.so",
"libaudio.so",
"libicui18n.so",
"libicuuc.so",
"libmiles.so",
"libopenvr_api.so",
"liboverride.so",
"libsteam.so",
"libtier0_s.so",
"libv8.so",
"libvideo.so",
"libvstdlib_s.so",
"serverbrowser.so",
"steamclient.so",
"steamoverlayvulkanlayer.so",
"steamservice.so",
"steamui.so",
"vgui2_s.so",
/* big picture mode */
"panorama",
"libpangoft2-1.0.so",
"libpango-1.0.so",
/* steamwebhelper */
"libcef.so",
};
/* Find out if it exists */
bool file_exists = nc_file_exists(name);
/* Find out if its a Steam private lib.. These are relative "./" files too! */
if (name && (strstr(name, "/Steam/") || strncmp(name, "./", 2) == 0)) {
for (size_t i = 0; i < ARRAY_SIZE(steam_allowed); i++) {
if (strstr(name, steam_allowed[i])) {
return (char *)name;
}
}
/* If LSI_DEBUG is set, spam it. */
if (getenv("LSI_DEBUG") && file_exists) {
emit_overriden_lib(name);
}
return NULL;
}
return (char *)name;
}
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 8
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* vi: set shiftwidth=8 tabstop=8 expandtab:
* :indentSize=8:tabSize=8:noTabs=true:
*/
+15
View File
@@ -0,0 +1,15 @@
# Only build libintercept if told to!
if with_libintercept == true
intercept_sources = [
'main.c',
]
main_intercept = shared_library(
'lsi-intercept',
sources: intercept_sources + nica_sources,
include_directories: nica_includes,
install: true,
)
endif
Submodule
+1
Submodule src/libnica added at e71a442667
+12 -3
View File
@@ -1,7 +1,7 @@
/*
* This file is part of linux-steam-integration.
*
* Copyright © 2016 Ikey Doherty
* Copyright © 2016-2017 Ikey Doherty <ikey@solus-project.com>
*
* linux-steam-integration is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License as
@@ -143,6 +143,13 @@ bool lsi_config_load(LsiConfig *config)
map_val = NULL;
}
/* Do we want libintercept? */
map_val = nc_hashmap_get(nc_hashmap_get(mconfig, "Steam"), "use-libintercept");
if (map_val) {
config->use_libintercept = lsi_is_boolean_true(map_val);
map_val = NULL;
}
/* Check if 32-bit is being forced */
map_val = nc_hashmap_get(nc_hashmap_get(mconfig, "Steam"), "force-32bit");
if (map_val) {
@@ -178,9 +185,10 @@ bool lsi_config_store(LsiConfig *config)
return false;
}
if (fprintf(fp,
"[Steam]\nuse-native-runtime = %s\nforce-32bit = %s\n",
"[Steam]\nuse-native-runtime = %s\nforce-32bit = %s\nuse-libintercept = %s\n",
lsi_bool_to_string(config->use_native_runtime),
lsi_bool_to_string(config->force_32)) < 0) {
lsi_bool_to_string(config->force_32),
lsi_bool_to_string(config->use_libintercept)) < 0) {
return false;
}
return true;
@@ -194,6 +202,7 @@ void lsi_config_load_defaults(LsiConfig *config)
* things that LSI knows about */
config->force_32 = false;
config->use_native_runtime = true;
config->use_libintercept = true;
}
void lsi_report_failure(const char *s, ...)
+4 -9
View File
@@ -1,7 +1,7 @@
/*
* This file is part of linux-steam-integration.
*
* Copyright © 2016 Ikey Doherty
* Copyright © 2016-2017 Ikey Doherty <ikey@solus-project.com>
*
* linux-steam-integration is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License as
@@ -16,25 +16,20 @@
#include <fcntl.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include "../common.h"
#include "config.h"
/* Mark a variable/argument as unused to skip warnings */
#define __lsi_unused__ __attribute__((unused))
/* Force inlining of a function */
#define __lsi_inline__ __attribute__((always_inline))
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
/**
* Current Linux Steam Integration settings.
*/
typedef struct LsiConfig {
bool force_32; /**<Do we force 32-bit? */
bool use_native_runtime; /**<Do we force our native runtime? */
bool use_libintercept; /**<Do we force libintercept? */
} LsiConfig;
/**
+18
View File
@@ -0,0 +1,18 @@
# Only build liblsi if told to!
if shim_behaviour != 'none' or with_frontend == true
lsi_sources = [
'lsi.c',
]
lib_lsi = static_library(
'lsi-lsi',
sources: lsi_sources + nica_sources,
include_directories: nica_includes,
)
link_lsi = declare_dependency(
link_with: lib_lsi,
include_directories: include_directories('.'),
)
endif
+22
View File
@@ -0,0 +1,22 @@
# Track how to build nica, as we actually build it twice..
nica_sources = [
'../libnica/src/array.c',
'../libnica/src/hashmap.c',
'../libnica/src/files.c',
'../libnica/src/inifile.c',
'../libnica/src/list.c',
'../libnica/src/nc-string.c',
'../libnica/src/util.c',
]
nica_includes = [
include_directories('libnica/src/include'),
include_directories('libnica/src/include/nica'),
config_h_dir,
]
subdir('lsi')
subdir('frontend')
subdir('intercept')
subdir('shim')
-472
View File
@@ -1,472 +0,0 @@
/*
* This file is part of libnica.
*
* Copyright (C) 2016 Intel Corporation
*
* libnica is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*/
#define _GNU_SOURCE
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "hashmap.h"
#define INITIAL_SIZE 61
/* When we're "full", 70% */
#define FULL_FACTOR 0.7
/* Multiple to increase bucket size by */
#define INCREASE_FACTOR 4
/**
* An bucket/chain within the hashmap
*/
typedef struct NcHashmapEntry {
void *hash; /**<The key for this item */
void *value; /**<Value for this item */
struct NcHashmapEntry *next; /**<Next item in the chain */
bool occ; /**<Whether this bucket is occupied */
} NcHashmapEntry;
/**
* A NcHashmap
*/
struct NcHashmap {
int size; /**<Current size of the hashmap */
int next_size; /**<Next size at which we need to resize */
int n_buckets; /**<Current number of buckets */
NcHashmapEntry *buckets; /**<Stores our bucket chains */
nc_hash_create_func hash; /**<Hash generation function */
nc_hash_compare_func compare; /**<Key comparison function */
nc_hash_free_func key_free; /**<Cleanup function for keys */
nc_hash_free_func value_free; /**<Cleanup function for values */
};
/**
* Iteration object
*/
typedef struct _NcHashmapIter {
int bucket; /**<Current bucket position */
NcHashmap *map; /**<Associated NcHashmap */
void *item; /**<Current item in this iteration */
} _NcHashmapIter;
static void nc_hashmap_update_next_size(NcHashmap *self);
static bool nc_hashmap_resize(NcHashmap *self);
static inline bool nc_hashmap_maybe_resize(NcHashmap *self)
{
if (!self) {
return false;
}
if (self->size >= self->next_size) {
return true;
}
return false;
}
static NcHashmap *nc_hashmap_new_internal(nc_hash_create_func create, nc_hash_compare_func compare,
nc_hash_free_func key_free, nc_hash_free_func value_free)
{
NcHashmap *map = NULL;
NcHashmapEntry *buckets = NULL;
map = calloc(1, sizeof(NcHashmap));
if (!map) {
return NULL;
}
buckets = calloc(INITIAL_SIZE, sizeof(NcHashmapEntry));
if (!buckets) {
free(map);
return NULL;
}
map->buckets = buckets;
map->n_buckets = INITIAL_SIZE;
map->hash = create ? create : nc_simple_hash;
map->compare = compare ? compare : nc_simple_compare;
map->key_free = key_free;
map->value_free = value_free;
map->size = 0;
nc_hashmap_update_next_size(map);
return map;
}
NcHashmap *nc_hashmap_new(nc_hash_create_func create, nc_hash_compare_func compare)
{
return nc_hashmap_new_internal(create, compare, NULL, NULL);
}
NcHashmap *nc_hashmap_new_full(nc_hash_create_func create, nc_hash_compare_func compare,
nc_hash_free_func key_free, nc_hash_free_func value_free)
{
return nc_hashmap_new_internal(create, compare, key_free, value_free);
}
static inline unsigned nc_hashmap_get_hash(NcHashmap *self, const void *key)
{
unsigned hash = self->hash(key);
return hash;
}
static bool nc_hashmap_insert_bucket(NcHashmap *self, NcHashmapEntry *buckets, int n_buckets,
unsigned hash, const void *key, void *value)
{
NcHashmapEntry *row = &(buckets[hash % n_buckets]);
NcHashmapEntry *head = NULL;
NcHashmapEntry *parent = head = row;
bool can_replace = false;
NcHashmapEntry *tomb = NULL;
int ret = 1;
while (row) {
if (!row->occ) {
tomb = row;
}
parent = row;
if (row->occ && row->hash == key) {
if (self->compare(row->hash, key)) {
can_replace = true;
break;
}
}
row = row->next;
}
if (can_replace) {
/* Replace existing allocations. */
if (self->value_free) {
self->value_free(row->value);
}
if (self->key_free) {
self->key_free(row->hash);
}
ret = 0;
} else if (tomb) {
row = tomb;
}
if (!row) {
row = calloc(1, sizeof(NcHashmapEntry));
if (!row) {
return -1;
}
}
row->hash = (void *)key;
row->value = value;
row->occ = true;
if (parent != row && parent) {
parent->next = row;
}
return ret;
}
bool nc_hashmap_put(NcHashmap *self, const void *key, void *value)
{
if (!self) {
return false;
}
int inc;
if (nc_hashmap_maybe_resize(self)) {
if (!nc_hashmap_resize(self)) {
return false;
}
}
unsigned hash = nc_hashmap_get_hash(self, key);
inc = nc_hashmap_insert_bucket(self, self->buckets, self->n_buckets, hash, key, value);
if (inc > 0) {
self->size += inc;
return true;
} else {
return false;
}
}
static NcHashmapEntry *nc_hashmap_get_entry(NcHashmap *self, const void *key)
{
if (!self) {
return NULL;
}
unsigned hash = nc_hashmap_get_hash(self, key);
NcHashmapEntry *row = &(self->buckets[hash % self->n_buckets]);
while (row) {
if (self->compare(row->hash, key)) {
return row;
}
row = row->next;
}
return NULL;
}
void *nc_hashmap_get(NcHashmap *self, const void *key)
{
if (!self) {
return NULL;
}
NcHashmapEntry *row = nc_hashmap_get_entry(self, key);
if (row) {
return row->value;
}
return NULL;
}
static bool nc_hashmap_remove_internal(NcHashmap *self, const void *key, bool remove)
{
if (!self) {
return false;
}
NcHashmapEntry *row = nc_hashmap_get_entry(self, key);
if (!row) {
return false;
}
if (remove) {
if (self->key_free) {
self->key_free(row->hash);
}
if (self->value_free) {
self->value_free(row->value);
}
}
self->size -= 1;
row->hash = NULL;
row->value = NULL;
row->occ = false;
return true;
}
bool nc_hashmap_steal(NcHashmap *self, const void *key)
{
return nc_hashmap_remove_internal(self, key, false);
}
bool nc_hashmap_remove(NcHashmap *self, const void *key)
{
return nc_hashmap_remove_internal(self, key, true);
}
bool nc_hashmap_contains(NcHashmap *self, const void *key)
{
return (nc_hashmap_get(self, key)) != NULL;
}
static inline void nc_hashmap_free_bucket(NcHashmap *self, NcHashmapEntry *bucket, bool nuke)
{
if (!self) {
return;
}
NcHashmapEntry *tmp = bucket;
NcHashmapEntry *bk = bucket;
NcHashmapEntry *root = bucket;
while (tmp) {
bk = NULL;
if (tmp->next) {
bk = tmp->next;
}
if (nuke && tmp->occ) {
if (self->key_free) {
self->key_free(tmp->hash);
}
if (self->value_free) {
self->value_free(tmp->value);
}
}
if (tmp != root) {
free(tmp);
}
tmp = bk;
}
}
void nc_hashmap_free(NcHashmap *self)
{
if (!self) {
return;
}
for (int i = 0; i < self->n_buckets; i++) {
NcHashmapEntry *row = &(self->buckets[i]);
nc_hashmap_free_bucket(self, row, true);
}
if (self->buckets) {
free(self->buckets);
}
free(self);
}
static void nc_hashmap_update_next_size(NcHashmap *self)
{
if (!self) {
return;
}
self->next_size = (int)(self->n_buckets * FULL_FACTOR);
}
int nc_hashmap_size(NcHashmap *self)
{
if (!self) {
return -1;
}
return self->size;
}
static bool nc_hashmap_resize(NcHashmap *self)
{
if (!self || !self->buckets) {
return false;
}
NcHashmapEntry *old_buckets = self->buckets;
NcHashmapEntry *new_buckets = NULL;
NcHashmapEntry *entry = NULL;
int incr;
int old_size, new_size;
int items = 0;
new_size = old_size = self->n_buckets;
new_size *= INCREASE_FACTOR;
new_buckets = calloc(new_size, sizeof(NcHashmapEntry));
if (!new_buckets) {
return false;
}
for (int i = 0; i < old_size; i++) {
entry = &(old_buckets[i]);
while (entry) {
if (entry->occ) {
unsigned hash = nc_hashmap_get_hash(self, entry->hash);
if ((incr = nc_hashmap_insert_bucket(self,
new_buckets,
new_size,
hash,
entry->hash,
entry->value)) > 0) {
items += incr;
} else {
/* Likely a memory issue */
goto failure;
}
}
entry = entry->next;
}
}
/* Successfully resized - do this separately because we need to
* gaurantee old data is preserved */
for (int i = 0; i < old_size; i++) {
nc_hashmap_free_bucket(self, &(old_buckets[i]), false);
}
free(old_buckets);
self->n_buckets = new_size;
self->size = items;
self->buckets = new_buckets;
nc_hashmap_update_next_size(self);
return true;
failure:
for (int i = 0; i < new_size; i++) {
nc_hashmap_free_bucket(self, &(new_buckets[i]), true);
}
free(new_buckets);
return false;
}
void nc_hashmap_iter_init(NcHashmap *map, NcHashmapIter *citer)
{
_NcHashmapIter *iter = NULL;
if (!map || !citer) {
return;
}
iter = (_NcHashmapIter *)citer;
_NcHashmapIter it = {
.bucket = -1, .map = map, .item = NULL,
};
*iter = it;
}
bool nc_hashmap_iter_next(NcHashmapIter *citer, void **key, void **value)
{
_NcHashmapIter *iter = NULL;
NcHashmapEntry *item = NULL;
NcHashmap *map = NULL;
int n_buckets = 0;
if (!citer) {
return false;
}
iter = (_NcHashmapIter *)citer;
map = iter->map;
if (!map) {
return false;
}
n_buckets = map->n_buckets;
item = iter->item;
for (;;) {
if (iter->bucket >= n_buckets) {
if (item && !item->next) {
return false;
}
}
if (!item) {
iter->bucket++;
if (iter->bucket > n_buckets - 1) {
return false;
}
item = &(map->buckets[iter->bucket]);
}
if (item && item->occ) {
goto success;
}
item = item->next;
}
return false;
success:
iter->item = item->next;
if (key) {
*key = item->hash;
}
if (value) {
*value = item->value;
}
return true;
}
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 8
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* vi: set shiftwidth=8 tabstop=8 expandtab:
* :indentSize=8:tabSize=8:noTabs=true:
*/
-239
View File
@@ -1,239 +0,0 @@
/*
* This file is part of libnica.
*
* Copyright (C) 2016 Intel Corporation
*
* libnica is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This is a chained Hashmap implementation. It focuses on being clean
* and efficient, and is comparable (at -O2) to open addressing hashmaps
* up until around 105,000 elements, where open addressing will begin
* to come out in front. At 1 million elements, open addressing is a clear
* winner, however currently we only need a maximum of 70k elements in
* our implementation.
*
* Given the memory required to even begin to deal with 1 million elements,
* it becomes very questionable whether a hashmap should have even been
* used in the first place (mmap'd db?)
*/
#pragma once
#define _GNU_SOURCE
#include <stdbool.h>
#include <stdint.h>
#include "util.h"
/* Convert between uint and void* */
#define NC_HASH_KEY(x) ((void *)((uintptr_t)(x)))
#define NC_HASH_VALUE(x) NC_HASH_KEY(x)
#define NC_UNHASH_KEY(x) ((unsigned int)((uintptr_t)(x)))
#define NC_UNHASH_VALUE(x) NC_UNHASH_KEY(x)
typedef struct NcHashmap NcHashmap;
/**
* Iteration object
*/
typedef struct NcHashmapIter {
int n0;
void *n1;
void *n2;
} NcHashmapIter;
/**
* Hash comparison function definition
*
* @param l First value to compare
* @param r Second value to compare
*
* @return true if l and r both match, otherwise false
*/
typedef bool (*nc_hash_compare_func)(const void *l, const void *r);
/**
* Hash creation function definition
*
* @param key Key to generate a hash for
*
* @return an unsigned integer hash result
*/
typedef unsigned (*nc_hash_create_func)(const void *key);
/**
* Callback definition to free keys and values
*
* @param p Single-depth pointer to either a key or value that should be freed
*/
typedef void (*nc_hash_free_func)(void *p);
/**
* Default hash/comparison functions
*
* @note These are only used for comparison of *keys*, not values. Unless
* explicitly using string keys, you should most likely stick with the default
* nc_simple_hash and nc_simple_compare functions
*/
/* Default string hash */
static inline unsigned nc_string_hash(const void *key)
{
unsigned hash = 5381;
const signed char *c;
/* DJB's hash function */
for (c = key; *c != '\0'; c++) {
hash = (hash << 5) + hash + (unsigned)*c;
}
return hash;
}
/**
* Trivial pointer->uint hash
*/
static inline unsigned nc_simple_hash(const void *source)
{
return NC_UNHASH_KEY(source);
}
/**
* Comparison of string keys
*/
static inline bool nc_string_compare(const void *l, const void *r)
{
if (!l || !r) {
return false;
}
return (strcmp(l, r) == 0);
}
/**
* Trivial pointer comparison
*/
static inline bool nc_simple_compare(const void *l, const void *r)
{
return (l == r);
}
/**
* Create a new NcHashmap
*
* @param hash Hash creation function
* @param compare Key comparison function
*
* @return A newly allocated NcHashmap
*/
NcHashmap *nc_hashmap_new(nc_hash_create_func hash, nc_hash_compare_func compare);
/**
* Create a new NcHashmap with cleanup functions
*
* @param hash Hash creation function
* @param compare Key comparison function
* @param key_free Function to free keys when removed/destroyed
* @param value_free Function to free values when removed/destroyed
*
* @return A newly allocated NcHashmap
*/
NcHashmap *nc_hashmap_new_full(nc_hash_create_func hash, nc_hash_compare_func compare,
nc_hash_free_func key_free, nc_hash_free_func value_free);
/**
* Store a key/value pair in the hashmap
*
* @note This will displace duplicate keys, and may free both the key
* and value if key_free and value_free are non null
*
* @param key Key to store in the hashmap
* @param value Value to be associated with the key
*
* @return true if the operation succeeded.
*/
bool nc_hashmap_put(NcHashmap *map, const void *key, void *value);
/**
* Get the value associated with the unique key
*
* @param key Unique key to obtain a value for
* @return The associated value if it exists, otherwise NULL
*/
void *nc_hashmap_get(NcHashmap *map, const void *key);
/**
* Determine if the key has an associated value in the NcHashmap
*
* @param key Unique key to check value for
* @return True if the key exists in the hashmap
*/
bool nc_hashmap_contains(NcHashmap *self, const void *key);
/**
* Free the given NcHashmap, and all keys/values if appropriate
*/
void nc_hashmap_free(NcHashmap *map);
/**
* Remove the value and key identified by key.
*
* @note If key_free or value_free are non-NULL, they will be invoked
* for both the key and value being removed
*
* @param key The unique key to remove
* @return true if the key/value pair were removed
*/
bool nc_hashmap_remove(NcHashmap *map, const void *key);
/**
* Remove the value and key identified by key without freeing them
*
* @return true if the key/value pair were stolen
*/
bool nc_hashmap_steal(NcHashmap *map, const void *key);
/**
* Return the current size of the hashmap
*
* @return size of the current hashmap (element count)
*/
int nc_hashmap_size(NcHashmap *map);
/**
* Initialise a NcHashmapIter for iteration purposes
*
* @note The iter *must* be re-inited to re-use, or if it becomes
* exhausted from a previous iteration run
*
* @param iter pointer to a NcHashmapIter
*/
void nc_hashmap_iter_init(NcHashmap *map, NcHashmapIter *iter);
/**
* Iterate every key/value pair in the hashmap
*
* @param iter A correctly initialised NcHashmapIter
* @param key Pointers to store the key in, may be NULL to skip
* @param value Pointer to store the value in, may be NULL to skip
*
* @return true if it's possible to iterate
*/
bool nc_hashmap_iter_next(NcHashmapIter *iter, void **key, void **value);
DEF_AUTOFREE(NcHashmap, nc_hashmap_free)
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 8
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* vi: set shiftwidth=8 tabstop=8 expandtab:
* :indentSize=8:tabSize=8:noTabs=true:
*/
-341
View File
@@ -1,341 +0,0 @@
/*
* This file is part of libnica.
*
* Copyright (C) 2016 Intel Corporation
*
* libnica is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*/
#define _GNU_SOURCE
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "hashmap.h"
#include "inifile.h"
/**
* Mapping of @NcIniError to static strings
*/
static const char *_errors[] = {[NC_INI_ERROR_FILE] = "",
[NC_INI_ERROR_EMPTY_KEY] = "Encountered empty key",
[NC_INI_ERROR_NOT_CLOSED] = "Expected closing \']\' for section",
[NC_INI_ERROR_NO_SECTION] =
"Encountered key=value mapping without a valid section",
[NC_INI_ERROR_INVALID_LINE] = "Expected key=value notation" };
const char *nc_ini_error(NcIniError error)
{
int j = abs(error);
if (j < NC_INI_ERROR_FILE || j >= NC_INI_ERROR_MAX) {
return "[Unknown Error]";
}
return _errors[j];
}
/**
* Strip leading whitespace from string (left)
*/
static char *lstrip(char *str, size_t len, size_t *out_len)
{
size_t skip_len = 0;
for (size_t i = 0; i < len; i++) {
if (str[i] == ' ' || str[i] == '\t') {
++skip_len;
continue;
}
break;
}
*out_len = len - skip_len;
if (skip_len > 0) {
char *c = strdup(str + skip_len);
free(str);
return c;
}
return str + skip_len;
}
/**
* Strip trailing whitespace from string (right)
*/
static char *rstrip(char *str, size_t len, size_t *out_len)
{
size_t skip_len = 0;
for (int i = len; i > 0; i--) {
if (str[i] == ' ' || str[i] == '\t') {
++skip_len;
continue;
} else if (str[i] == '\0') {
continue;
}
break;
}
*out_len = len - skip_len;
if (len == skip_len) {
return str;
}
str[(len - skip_len)] = '\0';
return str;
}
/**
* Chew both ends of a null terminated string of whitespace
* If the left side is whitespace padded, this will always result
* in a new allocation due to fast-forwarding the pointer. Keep
* this in mind.
*/
static char *string_chew_terminated(char *inp)
{
int skip_offset = 0;
int len = 0;
int end_len = 0;
bool count = true;
if (!inp) {
return NULL;
}
for (char *c = inp; *c; c++) {
if (count && (*c == ' ' || *c == '\t')) {
++skip_offset;
continue;
} else {
count = false;
}
++len;
}
for (int i = len; i > skip_offset; i--) {
if (inp[i] == ' ' || inp[i] == '\t') {
++end_len;
continue;
} else if (inp[i] == '\0') {
continue;
}
break;
}
if (end_len > 0) {
inp[(len - end_len)] = '\0';
}
if (skip_offset > 0) {
char *c = strdup(inp + skip_offset);
free(inp);
return c;
}
return inp;
}
/**
* Munch both ends of the string
*/
static char *string_strip(char *str, size_t len, size_t *out_len)
{
size_t mylen = len;
char *c = lstrip(str, len, &mylen);
c = rstrip(c, mylen, &mylen);
if (out_len) {
*out_len = mylen;
}
return c;
}
NcHashmap *nc_ini_file_parse(const char *path)
{
NcHashmap *ret = NULL;
int error_line = 0;
int r = 0;
r = nc_ini_file_parse_full(path, &ret, &error_line);
if (r != 0) {
if (abs(r) == NC_INI_ERROR_FILE) {
fprintf(stderr, "[inifile] %s: %s\n", strerror(errno), path);
} else {
fprintf(stderr,
"[inifile] %s [L%d]: %s\n",
nc_ini_error(r),
error_line,
path);
}
return NULL;
}
return ret;
}
int nc_ini_file_parse_full(const char *path, NcHashmap **out_map, int *error_line_number)
{
autofree(FILE) *file = NULL;
char *buf = NULL;
ssize_t r = 0;
size_t sn = 0;
int line_count = 1;
char *current_section = NULL;
NcHashmap *root_map = NULL;
NcHashmap *section_map = NULL;
bool failed = false;
int err_ret = 0;
file = fopen(path, "r");
if (!file) {
return -(NC_INI_ERROR_FILE);
}
if (!out_map) {
fprintf(stderr, "nc_ini_file_parse_full(): NcHashmap pointer invalid\n");
return -1;
}
root_map = nc_hashmap_new_full(nc_string_hash,
nc_string_compare,
free,
(nc_hash_free_func)nc_hashmap_free);
while ((r = getline(&buf, &sn, file)) != -1) {
char *ch = NULL;
size_t str_len = r;
/* Fix newline */
if (buf[r - 1] == '\n') {
buf[r - 1] = '\0';
--r;
}
str_len = r;
buf = string_strip(buf, r, &str_len);
/* Empty lines are fine */
if (streq(buf, "")) {
goto next;
}
if (buf[0] == '[') {
/* Validate section start */
if (buf[str_len - 1] != ']') {
/* Throw error */
err_ret = NC_INI_ERROR_NOT_CLOSED;
goto fail;
}
/* Grab the section name, and "close" last section */
buf[str_len - 1] = '\0';
if (current_section) {
free(current_section);
}
current_section = strdup(buf + 1);
section_map = nc_hashmap_get(root_map, current_section);
if (!section_map) {
/* Create a new section dynamically */
section_map = nc_hashmap_new_full(nc_string_hash,
nc_string_compare,
free,
free);
nc_hashmap_put(root_map, strdup(current_section), section_map);
}
goto next;
} else if (buf[0] == '#' || buf[0] == ';') {
/* Skip comment */
goto next;
}
/* Look for key = value */
ch = strchr(buf, '=');
if (!ch) {
/* Throw error */
err_ret = NC_INI_ERROR_INVALID_LINE;
goto fail;
}
/* Can't have sectionless k->v */
if (!current_section) {
err_ret = NC_INI_ERROR_NO_SECTION;
goto fail;
}
int offset = ch - buf;
/* Grab the key->value from this assignment line */
char *value = strdup((buf + offset) + 1);
buf[offset] = '\0';
char *key = strdup(buf);
key = string_chew_terminated(key);
value = string_chew_terminated(value);
if (streq(key, "")) {
err_ret = NC_INI_ERROR_EMPTY_KEY;
free(key);
free(value);
goto fail;
}
/* Ensure a section mapping exists */
section_map = nc_hashmap_get(root_map, current_section);
if (!section_map) {
err_ret = 1;
fprintf(stderr,
"[inifile] Fatal! No section map for named "
"section: %s\n",
current_section);
abort();
}
/* Insert these guys into the map */
if (!nc_hashmap_put(section_map, key, value)) {
err_ret = 1;
fprintf(stderr, "[inifile] Fatal! Out of memory\n");
abort();
}
/* Progression + cleanup */
next:
if (buf) {
free(buf);
buf = NULL;
}
++line_count;
sn = 0;
continue;
/* Parsing error, bail */
fail:
failed = true;
if (error_line_number) {
*error_line_number = line_count;
}
break;
}
if (buf) {
free(buf);
buf = NULL;
}
if (current_section) {
free(current_section);
current_section = NULL;
}
if (failed) {
if (root_map) {
nc_hashmap_free(root_map);
}
return -(err_ret);
}
*out_map = root_map;
return 0;
}
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 8
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* vi: set shiftwidth=8 tabstop=8 expandtab:
* :indentSize=8:tabSize=8:noTabs=true:
*/
-111
View File
@@ -1,111 +0,0 @@
/*
* This file is part of libnica.
*
* Copyright (C) 2016 Intel Corporation
*
* libnica is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*/
#pragma once
#include "hashmap.h"
#include "util.h"
/**
* The Nica INI Parser focuses on simplicity and ease of use, therefore it
* currently only supports a read-only parse operation.
*
* The parse functions are used to return a _root_ @NcHashmap which contains
* name to section mappings. Each section is in turn an @NcHashmap with string
* to string mappings.
*
* Consider the following example:
*
* [Person]
* alive = true
* ; This person is alive
*
* The returned @NcHashmap will contain the key "Person", which corresponds to
* another @NcHashmap. This @NcHashmap will contain the key "alive", which has
* the value "true". Therefore we can access this variable using the @NcHashmap
* API:
*
* char *alive = nc_hashmap_get(nc_hashmap_get(ini, "Person"), "alive");
*
* Conversely, we can use the API to determine whether a section or key is set:
*
* if (nc_hashmap_contains(ini, "Person"))
*
* As @NcHashmap returns NULL in nc_hashmap_get, you can happily pass NULL
* keys, allowing quick key fetches without checking if the section exists.
*
* In our INI file, lines beginning with ";" or "#" are considered as comments
* and are not processed. A line following the "key = value" notation is an
* _assignment_. "[Section]" is considered to be a section in the INI file, and
* all INI files must have at least one section.
*
* We do allow duplication section definitions, by following a merge policy. If
* a key is redefined then the original key->value mapping is freed and the new
* key->value mapping is inserted.
*
* The parser will return a correctly configured @NcHashmap, the only cleanup
* required is to call nc_hashmap_free on it. Alternatively, use the autofree
* helper, for a RAII approach:
*
* autofree(NcHashmap) *map = nc_ini_file_parse("myfile.ini");
*/
/**
* Errors that are reported when parsing an ini file
*/
typedef enum {
NC_INI_ERROR_MIN = 0,
NC_INI_ERROR_FILE, /**< File based error, check strerror(errno) */
NC_INI_ERROR_EMPTY_KEY, /**< Empty key in an assignment line */
NC_INI_ERROR_NOT_CLOSED, /**< Encountered section start that wasn't closed with a ']' */
NC_INI_ERROR_NO_SECTION, /**< Key assignment with no defined sections */
NC_INI_ERROR_INVALID_LINE, /**< Encountered an invalid line (syntax) */
NC_INI_ERROR_MAX
} NcIniError;
/**
* Convenience wrapper for nc_ini_file_parse_full, reports errors to stderrr
* and returns an NcHashmap if the parsing succeeded
*/
NcHashmap *nc_ini_file_parse(const char *path);
/**
* Parse an INI file into a hash of hashes.
*
* @param path Path on the filesystem to parse, must be in INI syntax.
* @param out_map Pointer to store the resulting root NcHashmap in
* @param error_line_number If not null, then the erronous line number is
* stored.
*
* @return 0 if the call was succesful, or a negative integer. See nc_ini_error
*/
int nc_ini_file_parse_full(const char *path, NcHashmap **out_map, int *error_line_number);
/**
* Return a string representation for a given @NcIniError
*
* @param error Valid NcIniError code
* @return a static string owned by the implementation
*/
const char *nc_ini_error(NcIniError error);
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 8
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* vi: set shiftwidth=8 tabstop=8 expandtab:
* :indentSize=8:tabSize=8:noTabs=true:
*/
-53
View File
@@ -1,53 +0,0 @@
/*
* This file is part of libnica.
*
* Copyright (C) 2016 Intel Corporation
*
* libnica is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*/
#pragma once
#define _GNU_SOURCE
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define DEF_AUTOFREE(N, C) \
static inline void _autofree_func_##N(void *p) \
{ \
if (p && *(N **)p) { \
C(*(N **)p); \
(*(void **)p) = NULL; \
} \
}
#define autofree(N) __attribute__((cleanup(_autofree_func_##N))) N
/**
* Dump any leaked file descriptors
*/
void nc_dump_file_descriptor_leaks(void);
#define streq(x, y) strcmp(x, y) == 0 ? true : false
DEF_AUTOFREE(char, free)
DEF_AUTOFREE(FILE, fclose)
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 8
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* vi: set shiftwidth=8 tabstop=8 expandtab:
* :indentSize=8:tabSize=8:noTabs=true:
*/
+21
View File
@@ -0,0 +1,21 @@
if shim_behaviour == 'replacement'
shim_target_name = 'steam'
else
shim_target_name = 'lsi-steam'
endif
if shim_behaviour != 'none'
shim_sources = [
'shim.c',
]
shim = executable(
shim_target_name,
sources: shim_sources + nica_sources,
include_directories: nica_includes,
dependencies: [
link_lsi,
],
install: true,
)
endif
+33 -7
View File
@@ -1,7 +1,7 @@
/*
* This file is part of linux-steam-integration.
*
* Copyright © 2016 Ikey Doherty
* Copyright © 2016-2017 Ikey Doherty <ikey@solus-project.com>
*
* linux-steam-integration is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License as
@@ -26,16 +26,29 @@
*/
#define EMUL32BIN "linux32"
/**
* Audit path is used for the libintercept library to ensure Steam only uses the
* host SDL, etc.
*/
#define AUDIT_PATH "/usr/\$LIB/liblsi-intercept.so"
int main(int argc, char **argv)
{
LsiConfig config = { 0 };
bool is_x86_64;
const char *n_argv[argc + 2];
const char *exec_command = NULL;
/* Use the appropriate Steam depending on configuration */
const char *lsi_exec_bin = NULL;
int i = 1;
int8_t off = 0;
int (*vfunc)(const char *, char *const argv[]) = NULL;
#ifdef REPLACE_STEAM
lsi_exec_bin = STEAM_BINARY;
#else
lsi_exec_bin = "/usr/bin/steam";
#endif
/* Initialise config */
if (!lsi_config_load(&config)) {
lsi_config_load_defaults(&config);
@@ -43,8 +56,8 @@ int main(int argc, char **argv)
is_x86_64 = lsi_system_is_64bit();
if (!lsi_file_exists(STEAM_BINARY)) {
lsi_report_failure("Steam isn't currently installed at %s", STEAM_BINARY);
if (!lsi_file_exists(lsi_exec_bin)) {
lsi_report_failure("Steam isn't currently installed at %s", lsi_exec_bin);
return EXIT_FAILURE;
}
@@ -52,6 +65,12 @@ int main(int argc, char **argv)
if (config.use_native_runtime) {
/* Explicitly disable the runtime */
setenv("STEAM_RUNTIME", "0", 1);
#ifdef HAVE_LIBINTERCEPT
/* Only use libintercept in combination with native runtime! */
if (config.use_libintercept) {
setenv("LD_AUDIT", AUDIT_PATH, 1);
}
#endif
} else {
/* Only preload when needed. */
if (lsi_system_requires_preload()) {
@@ -64,20 +83,27 @@ int main(int argc, char **argv)
* usage of dbus by Steam. Help them out */
setenv("DBUS_FATAL_WARNINGS", "0", 1);
/* A recent regression is to interpret XMODIFIERS and then fail to
* make it beyond `SDL_InitSubSystem` due to `InitIME` failing to
* properly use D-BUS ...
*/
unsetenv("XMODIFIERS");
unsetenv("GTK_MODULES");
memset(&n_argv, 0, sizeof(char *) * (argc + 2));
/* If we're 64-bit and 32-bit is forced, proxy via linux32 */
if (config.force_32 && is_x86_64) {
exec_command = EMUL32BIN;
n_argv[0] = EMUL32BIN;
n_argv[1] = STEAM_BINARY;
n_argv[1] = lsi_exec_bin;
off = 1;
/* Use linux32 in the path */
vfunc = execvp;
} else {
/* Directly call STEAM_BINARY */
exec_command = STEAM_BINARY;
n_argv[0] = STEAM_BINARY;
/* Directly call lsi_exec_bin */
exec_command = lsi_exec_bin;
n_argv[0] = lsi_exec_bin;
/* Full path here due to shadow nature */
vfunc = execv;
}
Executable
+12
View File
@@ -0,0 +1,12 @@
#!/bin/bash
set -e
env -i DBUS_SESSION_ADDRESS=$DBUS_SESSION_ADDRESS \
PATH=$PATH \
DISPLAY=$DISPLAY \
HOME=$HOME \
USER=$USER \
USERNAME=$USERNAME \
LD_AUDIT="`pwd`/buildroot/install/usr/\$LIB/liblsi-intercept.so" \
LD_PRELOAD="/usr/\$LIB/libxcb.so.1:/usr/\$LIB/libX11.so.6:/usr/\$LIB/libstdc++.so.6" \
DBUS_FATAL_WARNINGS=0 \
STEAM_RUNTIME=0 /usr/lib64/steam/steam
+1 -1
View File
@@ -1,2 +1,2 @@
#!/bin/bash
clang-format -i $(find . -name '*.[ch]')
clang-format -i $(find . -not -path '*/libnica/*' -name '*.[ch]')