mirror of
https://github.com/clearlinux/linux-steam-integration.git
synced 2026-09-06 22:01:27 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d1b7a86867 | ||
|
|
0c252321e3 | ||
|
|
b9d677dee1 | ||
|
|
86fbf11040 | ||
|
|
ec67c801ad | ||
|
|
0a02c111a0 | ||
|
|
48a9bac406 | ||
|
|
8de8962099 | ||
|
|
de9380dc8d | ||
|
|
9bd423fb34 | ||
|
|
75d2e9cc45 | ||
|
|
7d0acc9891 | ||
|
|
7bae532d85 | ||
|
|
4f332c9acf | ||
|
|
f2dddd9f88 | ||
|
|
94d5c6d939 | ||
|
|
f4564e2ab8 | ||
|
|
8d85cb3bed | ||
|
|
fb2e84cb97 | ||
|
|
e1f2783d11 | ||
|
|
13afbe69c3 | ||
|
|
b458346f5a | ||
|
|
bf4fcfe533 | ||
|
|
b7ba890433 | ||
|
|
faa0c3a154 | ||
|
|
99e54dd74b | ||
|
|
5d18366a17 | ||
|
|
6154f2373c | ||
|
|
fe80861f66 | ||
|
|
38ec05ce98 | ||
|
|
d983f8ebdf | ||
|
|
4d3abc5e37 | ||
|
|
47e50b789f | ||
|
|
9905a2bc81 | ||
|
|
11e1d8ae5f | ||
|
|
00e78f705e | ||
|
|
54c685bb1a | ||
|
|
a61e22d677 | ||
|
|
93b0f82279 | ||
|
|
d870577a55 | ||
|
|
fd252e265d | ||
|
|
3684dfe11e | ||
|
|
d5b31dbf54 | ||
|
|
f6dcc30d33 | ||
|
|
7a2670f3e1 | ||
|
|
61d9296a6c | ||
|
|
f2bbe963b6 | ||
|
|
4547d8582a | ||
|
|
1db376fa38 | ||
|
|
0bf704fc79 | ||
|
|
cf023ffb6a | ||
|
|
4b36347ade | ||
|
|
d3469e3eb0 | ||
|
|
29081c492d | ||
|
|
ac76372b84 | ||
|
|
6b107f33b5 | ||
|
|
ba8ae8f384 | ||
|
|
d77ad58934 | ||
|
|
2aae934271 | ||
|
|
12d5268252 | ||
|
|
15253b97df | ||
|
|
8dd27f6f24 | ||
|
|
f21559ee78 | ||
|
|
5b17fe9447 | ||
|
|
9ca7ffdd85 | ||
|
|
3b552ec17d | ||
|
|
a3226a293c | ||
|
|
9c40a9d106 | ||
|
|
49d68c52a7 | ||
|
|
d20c230067 | ||
|
|
bd4707f3ca | ||
|
|
1a70578cb1 | ||
|
|
7995688ea2 | ||
|
|
e3360b7ddc | ||
|
|
1700f2616c | ||
|
|
6f8e905569 | ||
|
|
7c0ff5031e | ||
|
|
dd5237e648 | ||
|
|
2282296045 | ||
|
|
ba48264207 | ||
|
|
bddc0f29b1 | ||
|
|
196a039746 | ||
|
|
4ff98e225a | ||
|
|
e49a7873fe | ||
|
|
6659905102 | ||
|
|
16702d0d8f | ||
|
|
00d8831720 | ||
|
|
451ffe7680 |
+2
-18
@@ -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/
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
[submodule "src/libnica"]
|
||||
path = src/libnica
|
||||
url = git@github.com:intel/libnica.git
|
||||
-82
@@ -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)
|
||||
@@ -0,0 +1,239 @@
|
||||
linux-steam-integration
|
||||
-----------------------
|
||||
|
||||
Linux Steam Integration is a software package aimed squarely at Linux distribution developers.
|
||||
It is designed to offer a far better Steam* experience than the stock client, doing away with old
|
||||
integration issues and crashes.
|
||||
|
||||
### Native Libraries
|
||||
|
||||
LSI enforces the use of host-native libraries, both in the Steam Client (via `lsi-intercept`) and
|
||||
games themselves. This ensures Steam integrates properly, games run with the latest libraries
|
||||
and users get the best performance.
|
||||
|
||||
Currently the officially provided Steam runtime is based on very old Ubuntu 12.04 packages,
|
||||
and has some cherry-picked updates (SDL, for example). This causes lots of integration issues
|
||||
on most newer distributions, such as broken client behaviour with full screen videos, outright
|
||||
crashes on startup, mismatched C++ ABI, missing sound, ugly menus, broken font rendering,
|
||||
poor performance, etc.
|
||||
|
||||
In a nut shell, LSI will force most of Steam over to using native libraries. The intercept
|
||||
system is new as of 0.4, and will be extended in future to allow improving certain games
|
||||
on a process-name whitelisting basis.
|
||||
|
||||
### Configurable
|
||||
|
||||
You can easily toggle the use of the native runtime or the runtime provided with the official
|
||||
Steam package distribution. LSI doesn't butcher any files, so your local Steam directory does
|
||||
not need to be repaired or reinstalled. Just run `lsi-settings` and restart Steam.
|
||||
|
||||
Currently you can control whether the native runtime is used, force 32-bit mode for some troublesome
|
||||
ports, and control whether the intercept library is enabled (which will undo the various library
|
||||
mangling tricks of the Steam distribution to force the usage of the system libs via rtld-audit)
|
||||
|
||||
### Portable
|
||||
|
||||
You can (and should) stick this into any distro. Hit us up if you need help integrating, or need some
|
||||
portability changes made. We won't bite.
|
||||
|
||||
### Going beyond LSI
|
||||
|
||||
LSI solves a great many issues, however there is then still a responsibility for the integrating distribution
|
||||
to provide basic ABI compatibility, as well as being functionally comparable to the "stock" runtime.
|
||||
Our future plans will basically involve creating a project based around LSI and a specially produced,
|
||||
optimised Steam runtime, usable on all Linux distributions.
|
||||
|
||||
If you want to be part of that effort - and help bring a modern, optimised gaming experience to your Linux
|
||||
users, please get in contact. Let's do this once, and do it right.
|
||||
|
||||
### We're not them.
|
||||
|
||||
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/)
|
||||
|
||||

|
||||
|
||||
## Getting LSI
|
||||
|
||||
LSI may not yet be available for your distribution, however some community maintained repositories do exist.
|
||||
|
||||
- [Arch Linux](https://aur.archlinux.org/packages/linux-steam-integration/)
|
||||
- [Fedora](https://copr.fedorainfracloud.org/coprs/alunux/linux-steam-integration/)
|
||||
- Solus uses LSI by default, just install the steam package.
|
||||
|
||||
|
||||
## 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`
|
||||
|
||||
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-new-libcxx-abi=$boolean`
|
||||
|
||||
Enable this if your distribution uses a recent libstdc++ build, from
|
||||
GCC 6 or higher.
|
||||
|
||||
`-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 three 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.
|
||||
|
||||
`use-libintercept = $boolean`
|
||||
|
||||
If set to a true boolean value (yes/true/on), the `liblsi-intercept.so`
|
||||
library will be utilised to control dynamic linking within the Steam client
|
||||
and associated processes.
|
||||
|
||||
Note this requires `use-native-runtime` to be set.
|
||||
|
||||
The default value of this variable is true.
|
||||
|
||||
|
||||
## Common issues
|
||||
|
||||
Virtually all issues will come down to having the right dependencies. LSI puts an emphasis on
|
||||
host-first libraries, so make sure all the correct libraries are available for both 32-bit and
|
||||
64-bit.
|
||||
|
||||
Steam has both 32-bit and 64-bit processes/libraries, having the full dependency set for both
|
||||
the client and common games/engines is a must.
|
||||
|
||||
|
||||
### 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)
|
||||
|
||||
### Steam fails to launch with libintercept/native-runtime
|
||||
|
||||
Make sure you enable full debugging when first assigning dependencies to your runtime package:
|
||||
|
||||
```bash
|
||||
$ LSI_DEBUG=1 steam
|
||||
# If you use co-exist option:
|
||||
$ LSI_DEBUG=1 lsi-steam
|
||||
```
|
||||
|
||||
|
||||
## 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
@@ -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.
|
||||
@@ -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
@@ -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
@@ -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}
|
||||
])
|
||||
@@ -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
|
||||
@@ -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 +0,0 @@
|
||||
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
project(
|
||||
'linux-steam-integration',
|
||||
['c'],
|
||||
version: '0.4.1',
|
||||
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')
|
||||
|
||||
if get_option('with-new-libcxx-abi') == true
|
||||
cdata.set('LSI_USE_NEW_ABI', '1')
|
||||
endif
|
||||
|
||||
config_h = configure_file(
|
||||
configuration: cdata,
|
||||
output: 'config.h',
|
||||
)
|
||||
config_h_dir = include_directories('.')
|
||||
|
||||
subdir('data')
|
||||
subdir('src')
|
||||
@@ -0,0 +1,8 @@
|
||||
option('with-shim', type : 'combo', choices : ['replacement', 'co-exist', 'none'], value: '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)
|
||||
option('with-new-libcxx-abi', type: 'boolean', description: 'Enable when using the new libstdc++ ABI', value: false)
|
||||
Executable
+18
@@ -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 -Dwith-new-libcxx-abi=true
|
||||
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 -Dwith-new-libcxx-abi=true
|
||||
fi
|
||||
|
||||
ninja -C buildroot/build32
|
||||
ninja -C buildroot/build64
|
||||
|
||||
sudo ninja -C buildroot/build32 install
|
||||
sudo ninja -C buildroot/build64 install
|
||||
|
||||
|
||||
Executable
+14
@@ -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"
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* 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",
|
||||
"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:
|
||||
*/
|
||||
@@ -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
@@ -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, ...)
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
@@ -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
|
||||
@@ -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')
|
||||
@@ -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:
|
||||
*/
|
||||
@@ -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:
|
||||
*/
|
||||
@@ -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:
|
||||
*/
|
||||
@@ -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:
|
||||
*/
|
||||
@@ -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:
|
||||
*/
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -1,2 +1,2 @@
|
||||
#!/bin/bash
|
||||
clang-format -i $(find . -name '*.[ch]')
|
||||
clang-format -i $(find . -not -path '*/libnica/*' -name '*.[ch]')
|
||||
|
||||
Reference in New Issue
Block a user