mirror of
https://github.com/mumble-voip/mumble.git
synced 2025-10-26 11:19:16 +00:00
163 lines
9.8 KiB
Markdown
163 lines
9.8 KiB
Markdown
# Extending the Ice interface
|
|
|
|
The server component supports RPC via [ZeroC Ice](https://zeroc.com/products/ice). This document describes how the Ice interface of the server can be
|
|
extended.
|
|
|
|
Note: If not stated otherwise all referenced files live in `src/murmur/`.
|
|
|
|
The files involved in extending the Ice interface are
|
|
| **File** | **Description** |
|
|
| -------- | --------------- |
|
|
| `MumbleServer.ice` | Contains the formal definition of the interface |
|
|
| `MumbleServer.h` | Contains the C++ interface definition (abstract base classes). This file is generated based on `MumbleServer.ice` when invoking cmake (via `slice2cpp`). |
|
|
| `MumbleServer.cpp` | Contains some boilerplate and Ice-internal implementation code. This file is automatically generated. |
|
|
| `MumbleServerI.h` | Contains the definition of the actually implemented API classes (`ServerI` and `MetaI`). These extend the abstract base classes from `MumbleServer.h` |
|
|
| `MumbleServerWrapper.cpp` | Contains wrapper implementation of the `*I` API classes. This file is automatically generated. |
|
|
| `MumbleServer.h` | Contains the definition of a statically used helper class |
|
|
| `MumbleServer.cpp` | Contains the implementation of that helper class **and** _static_ functions used to actually implement the server-side functionality of the Ice API functions |
|
|
| `RPC.cpp` | Contains the implementations of the `Server` (the Mumble server, _not_ the Ice API type) class's member functions that are required to make certain functionality accessible to the static functions in `MumbleServerIce.cpp` |
|
|
|
|
All auto-generated functions will end up in the corresponding directory inside the `build` directory.
|
|
|
|
|
|
## Overview
|
|
|
|
The steps are
|
|
1. Add new function declarations to `MumbleServerI.h`
|
|
2. Write impl function in `MumbleServerIce.cpp`
|
|
3. Potentially write new public API functions (declare in `Server.h` and define in `RPC.cpp`
|
|
|
|
## Details
|
|
|
|
The `MumbleServerIceWrapper.cpp` file contains the `*_async` versions of the Ice callbacks that handle the async nature of these callbacks and also
|
|
contain the boilerplate for e.g. verification of the caller and things like this. Most importantly though these functions call the `impl_*` functions
|
|
defined in `MumbleServerIce.cpp`. For instance a function called `updateCertificates` inside the `Server` class will call
|
|
`impl_Server_updateCertificate` which has to be defined as a `static` function inside `MumbleServerIce.cpp`.
|
|
|
|
The declarations of the async functions generated this way are contained inside `MumbleServerI.h`. You have to manually add the function's declaration
|
|
into there. The easiest way to do this is to let the implementation be auto-generated and then copy the function signature from there into the
|
|
`MumbleServer.h` file (make the declaration `virtual` though).
|
|
|
|
The impl function's signature is always
|
|
```cpp
|
|
static void impl_<className>_<functionName>(const ::MumbleServer::AMD_<className>_<functionName>Ptr cb [, int server_id] [, <function arguments>]) {
|
|
// Implementation goes here
|
|
|
|
cb->ice_response([<function return value>]);
|
|
}
|
|
```
|
|
- `<className>`: Name of the class the function is declared in (e.g. `Server` or `Meta`)
|
|
- `<functionName>`: Name of the function as declared in the `MumbleServer.ice` file
|
|
- `[, int server_id]`: Only needed when extending the `Server` API class (the brackets are not part of what needs to be written in code)
|
|
- `[, <function arguments>]`: To be replaced by the list of arguments the function takes
|
|
- `[<function return value>]`: To be replaced with the value this function returns or to be removed if the function does not return anything.
|
|
|
|
|
|
If you have used non-default types that are declared in `MumbleServer.ice` (e.g. `IdList`), you can reference them here as
|
|
`::MumbleServer::<typeName>` (e.g. `::MumbleServer::IdList`).
|
|
|
|
Error reporting works via the `cb->ice_exception` function and if everything went well, the function must end by calling `cb->ice_response`
|
|
(potentially passing a value to that function that shall be returned to the caller of the function).
|
|
|
|
In general it is a good idea to have a look at the existing implementation inside `MumbleServerIce.cpp` and take inspiration from those.
|
|
|
|
Note that the implementations make heavy use of macros (e.g. `NEED_SERVER`, `NEED_CHANNEL`, etc.). These will initialize the corresponding variables
|
|
(`server`, `channel`, etc.) based on the parameters fed into the function (In order to obtain the channel, user, etc. you always have to initialize
|
|
the `server` variable first). For this to work it is essential that you are using the same parameter names as the existing implementations (e.g.
|
|
`server_id` for the server's ID). (For historical reasons the macro to obtain the `user` variable is called `NEED_PLAYER`)
|
|
|
|
Furthermore, you might notice the macro definitions like `ACCESS_Server_isListening_READ`. These are used to determine what kind of access privilege
|
|
execution of the associated Ice function is required. By default, all functions are assumed to require write-privilege in order to be able to be
|
|
executed. The general form of these macros is
|
|
```
|
|
ACCESS_<className>_<functionName>_<access>
|
|
```
|
|
where
|
|
- `<className>` and `<functionName>` are used as defined above. They define which function this macro will be associated with.
|
|
- `<access>` is the required access privilege. Possible values are `READ` and `ALL`, which allow function execution by anyone who has
|
|
read-privilege or simply everyone, regardless of their privileges.
|
|
The actual implementation of the access privilege checking is performed in the auto-generated `MumbleServerIceWrapper.cpp` based on whether an
|
|
associated macro is defined and if so, which one.
|
|
Please remember to `#undef` the respective macro at the end of the `MumbleServerIce.cpp` file in order to avoid macro definitions spilling into other
|
|
files when using unity builds.
|
|
|
|
|
|
If the function requires action on the server's side (beyond its public API), you have to declare a new public function in the `Server` class (this
|
|
time the Mumble server though; not the Ice server class) defined in `Server.h` (the definitions belong to the group of other RPC functions in there -
|
|
section marked by a comment). The implementation of this new function should then be written in `RPC.cpp`.
|
|
|
|
An example of when this is needed is for instance if you have to access the list of connected clients (e.g. because you want to send them a message).
|
|
While in the current state of the code it would be possible to access this list from the outside (public visibility), you should prefer creating a
|
|
public API function in the Mumble `Server` class that has the implementation in `RPC.cpp`.
|
|
|
|
## Testing Ice interface changes
|
|
|
|
So far, you've used `MumbleServer.ice` to modify and generate **server-side** code. The same file can be used to create Ice **clients**, which then
|
|
interact with the server. A small amount of configuration is required, namely:
|
|
|
|
| **Setting** | **Example** | **Description** |
|
|
| --- | --- | --- |
|
|
| `host` | `127.0.0.1` | The IP address (or domain) to which MumbleServer's Ice interface is bound. (Check [`murmur.ini`'s `ice` property `-h` flag](../../scripts/murmur.ini#L65).) |
|
|
| `port` | `6502` | The TCP port on which Ice's interface is listening. (Check [`murmur.ini`'s `ice` property `-p` flag](../../scripts/murmur.ini#L65).) |
|
|
| `secret` | `ice_pa55word` | A clear-text "password" used to authorize with the Ice server. (This will either be [`icesecretread`](../../scripts/murmur.ini#L79) or [`icesecretwrite`](../../scripts/murmur.ini#L80) from [`murmur.ini`](../../scripts/murmur.ini), with read-only or read-write privileges respectively.) |
|
|
| `slicefile` | `MumbleServer.ice` | The [`MumbleServer.ice`](../../src/murmur/MumbleServer.ice) file, containing any changes you intend to test. (This can be dynamically fetched from the Mumble server, provided it's running, has Ice exposed, and was built with the updated `MumbleServer.ice` file.) |
|
|
|
|
> :warning: Since the server's Ice interface is clear-text, there are security factors to consider. Use a strong-ish, unique secret, not used for
|
|
> any other case.
|
|
|
|
An existing Python Ice client is [`mice.py`](https://github.com/mumble-voip/mumble-scripts/blob/master/Helpers/mice.py), which simply creates
|
|
necessary Ice objects and then drops you into an interactive Python shell.
|
|
|
|
```python
|
|
# Make sure the Mumble server is running (in a separate terminal)
|
|
# $ ./mumble-server ...
|
|
|
|
# Grab mice.py
|
|
$ wget --quiet https://raw.githubusercontent.com/mumble-voip/mumble-scripts/master/Helpers/mice.py
|
|
|
|
# Either modify mice.py directly, or create mice_config.py with your proper settings
|
|
$ cat << EOF > mice_config.py
|
|
host = "127.0.0.1"
|
|
port = 6502
|
|
secret = "ice_pa55word"
|
|
prxstr = "Meta:tcp -h {} -p {} -t 1000".format(host, port)
|
|
slicefile = "MumbleServer.ice"
|
|
EOF
|
|
|
|
|
|
|
|
# Invoke Python and drop into interactive mode
|
|
$ ipython -i mice.py # IPython is very handy!
|
|
...
|
|
Import ice... Done
|
|
Trying to retrieve slice dynamically from server... Success
|
|
Import dynamically compiled murmur class... Done
|
|
Establish ice connection... [protected]... Done
|
|
MumbleServer object accessible via 'murmur' or 'm'
|
|
1 booted servers in 'sl', 's' contains 's/1 -t -e 1.0:tcp -h 127.0.0.1 -p 6502 -t 60000'
|
|
--- Reached interactive mode ---
|
|
|
|
In [1]: m, s # Represents "Meta" and (virtual) "Server" objects
|
|
Out[1]:
|
|
(Meta -t -e 1.0:tcp -h 127.0.0.1 -p 6502 -t 1000,
|
|
s/1 -t -e 1.0:tcp -h 127.0.0.1 -p 6502 -t 60000)
|
|
|
|
# Tab-complete to find interesting functions and play with Ice struct properties
|
|
In [2]: [(user.session, user.name) for user in s.getUsers().values()]
|
|
Out[2]: [(8L, 'Alice'), (7L, 'Bob')]
|
|
|
|
# IPython supports various inspection -- showing what it takes to kick a user
|
|
In [3]: s.kickUser?
|
|
Signature: s.kickUser(session, reason, context=None)
|
|
Docstring: <no docstring>
|
|
File: /tmp/tmpB0n19F.ice
|
|
Type: instancemethod
|
|
|
|
In [4]: s.kickUser(8, "Bye bye, Alice!")
|
|
|
|
In [5]: [(user.session, user.name) for user in s.getUsers().values()]
|
|
Out[5]: [(7L, 'Bob')]
|
|
```
|
|
|
|
|