Skip to content

Commit

Permalink
FEAT(server): Allow running while DB is in read-only mode
Browse files Browse the repository at this point in the history
Note that the server's feature set is reduced to the absolute minimum
and most requests to the server will simply be dropped. Only users
that have already been on the server when it entered this mode will be
able to stay on the server and keep talking to each other. No new
connections will be accepted and nobody can move channels or perform any
other noteworthy actions (except writing text messages, which will still
work).

This mode is explicitly meant to be used only for short periods of time
that might be needed to perform a database backup etc.
  • Loading branch information
Krzmbrzl committed Dec 24, 2024
1 parent fa1ff69 commit 6969661
Show file tree
Hide file tree
Showing 7 changed files with 194 additions and 28 deletions.
2 changes: 2 additions & 0 deletions src/Mumble.proto
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ message Reject {
// The user did not provide a certificate but one is required.
NoCertificate = 7;
AuthenticatorFail = 8;
// The server is currently not accepting new connections
NoNewConnections = 9;
}
// Rejection type.
optional RejectType type = 1;
Expand Down
17 changes: 17 additions & 0 deletions src/murmur/DBState.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright The Mumble Developers. All rights reserved.
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file at the root of the
// Mumble source tree or at <https://www.mumble.info/LICENSE>.

#ifndef MUMBLE_MURMUR_DBSTATE_H_
#define MUMBLE_MURMUR_DBSTATE_H_

/**
* Possible states the database can be in
*/
enum class DBState {
Normal,
ReadOnly,
};

#endif // MUMBLE_MURMUR_DBSTATE_H_
3 changes: 3 additions & 0 deletions src/murmur/Meta.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include "Timer.h"

#include "DBState.h"
#include "Version.h"

#ifdef Q_OS_WIN
Expand Down Expand Up @@ -184,6 +185,8 @@ class Meta : public QObject {
QString qsOS, qsOSVersion;
Timer tUptime;

DBState assumedDBState = DBState::Normal;

#ifdef Q_OS_WIN
static HANDLE hQoS;
#endif
Expand Down
67 changes: 41 additions & 26 deletions src/murmur/MumbleServer.ice
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,9 @@ module MumbleServer
UserList users;
};

/** Different states of the underlying database */
enum DBState { Normal, ReadOnly };

exception MurmurException {};
/** This is thrown when you specify an invalid session. This may happen if the user has disconnected since your last call to {@link Server.getUsers}. See {@link User.session} */
exception InvalidSessionException extends MurmurException {};
Expand All @@ -289,6 +292,8 @@ module MumbleServer
exception WriteOnlyException extends MurmurException {};
/** This is thrown when invalid input data was specified. */
exception InvalidInputDataException extends MurmurException {};
/** This is thrown when the server has its database in read-only mode and whatever you requested is incompatible with that. */
exception ReadOnlyModeException extends MurmurException {};

/** Callback interface for servers. You can supply an implementation of this to receive notification
* messages from the server.
Expand Down Expand Up @@ -468,16 +473,16 @@ module MumbleServer
idempotent bool isRunning() throws InvalidSecretException;

/** Start server. */
void start() throws ServerBootedException, ServerFailureException, InvalidSecretException;
void start() throws ServerBootedException, ServerFailureException, InvalidSecretException, ReadOnlyModeException;

/** Stop server.
* Note: Server will be restarted on Murmur restart unless explicitly disabled
* with setConf("boot", false)
*/
void stop() throws ServerBootedException, InvalidSecretException;
void stop() throws ServerBootedException, InvalidSecretException, ReadOnlyModeException;

/** Delete server and all it's configuration. */
void delete() throws ServerBootedException, InvalidSecretException;
void delete() throws ServerBootedException, InvalidSecretException, ReadOnlyModeException;

/** Fetch the server id.
*
Expand All @@ -504,41 +509,41 @@ module MumbleServer
*
* @param auth Authenticator object to perform subsequent authentications.
*/
void setAuthenticator(ServerAuthenticator *auth) throws ServerBootedException, InvalidCallbackException, InvalidSecretException;
void setAuthenticator(ServerAuthenticator *auth) throws ServerBootedException, InvalidCallbackException, InvalidSecretException, ReadOnlyModeException;

/** Retrieve configuration item.
* @param key Configuration key.
* @return Configuration value. If this is empty, see {@link Meta.getDefaultConf}
*/
idempotent string getConf(string key) throws InvalidSecretException, WriteOnlyException;
idempotent string getConf(string key) throws InvalidSecretException, WriteOnlyException, ReadOnlyModeException;

/** Retrieve all configuration items.
* @return All configured values. If a value isn't set here, the value from {@link Meta.getDefaultConf} is used.
*/
idempotent ConfigMap getAllConf() throws InvalidSecretException;
idempotent ConfigMap getAllConf() throws InvalidSecretException, ReadOnlyModeException;

/** Set a configuration item.
* @param key Configuration key.
* @param value Configuration value.
*/
idempotent void setConf(string key, string value) throws InvalidSecretException;
idempotent void setConf(string key, string value) throws InvalidSecretException, ReadOnlyModeException;

/** Set superuser password. This is just a convenience for using {@link updateRegistration} on user id 0.
* @param pw Password.
*/
idempotent void setSuperuserPassword(string pw) throws InvalidSecretException;
idempotent void setSuperuserPassword(string pw) throws InvalidSecretException, ReadOnlyModeException;

/** Fetch log entries.
* @param first Lowest numbered entry to fetch. 0 is the most recent item.
* @param last Last entry to fetch.
* @return List of log entries.
*/
idempotent LogList getLog(int first, int last) throws InvalidSecretException;
idempotent LogList getLog(int first, int last) throws InvalidSecretException, ReadOnlyModeException;

/** Fetch length of log
* @return Number of entries in log
*/
idempotent int getLogLen() throws InvalidSecretException;
idempotent int getLogLen() throws InvalidSecretException, ReadOnlyModeException;

/** Fetch all users. This returns all currently connected users on the server.
* @return List of connected users.
Expand Down Expand Up @@ -573,7 +578,7 @@ module MumbleServer
* append to the returned list before calling this method.
* @param bans List of bans.
*/
idempotent void setBans(BanList bans) throws ServerBootedException, InvalidSecretException;
idempotent void setBans(BanList bans) throws ServerBootedException, InvalidSecretException, ReadOnlyModeException;

/** Kick a user. The user is not banned, and is free to rejoin the server.
* @param session Connection ID of user. See {@link User.session}.
Expand Down Expand Up @@ -647,19 +652,19 @@ module MumbleServer
* @param state Channel state to set.
* @see getChannelState
*/
idempotent void setChannelState(Channel state) throws ServerBootedException, InvalidChannelException, InvalidSecretException, NestingLimitException;
idempotent void setChannelState(Channel state) throws ServerBootedException, InvalidChannelException, InvalidSecretException, NestingLimitException, ReadOnlyModeException;

/** Remove a channel and all its subchannels.
* @param channelid ID of Channel. See {@link Channel.id}.
*/
void removeChannel(int channelid) throws ServerBootedException, InvalidChannelException, InvalidSecretException;
void removeChannel(int channelid) throws ServerBootedException, InvalidChannelException, InvalidSecretException, ReadOnlyModeException;

/** Add a new channel.
* @param name Name of new channel.
* @param parent Channel ID of parent channel. See {@link Channel.id}.
* @return ID of newly created channel.
*/
int addChannel(string name, int parent) throws ServerBootedException, InvalidChannelException, InvalidSecretException, NestingLimitException;
int addChannel(string name, int parent) throws ServerBootedException, InvalidChannelException, InvalidSecretException, NestingLimitException, ReadOnlyModeException;

/** Send text message to channel or a tree of channels.
* @param channelid Channel ID of channel to send to. See {@link Channel.id}.
Expand All @@ -683,7 +688,7 @@ module MumbleServer
* @param groups List of groups on the channel.
* @param inherit Should this channel inherit ACLs from the parent channel?
*/
idempotent void setACL(int channelid, ACLList acls, GroupList groups, bool inherit) throws ServerBootedException, InvalidChannelException, InvalidSecretException;
idempotent void setACL(int channelid, ACLList acls, GroupList groups, bool inherit) throws ServerBootedException, InvalidChannelException, InvalidSecretException, ReadOnlyModeException;

/** Temporarily add a user to a group on a channel. This state is not saved, and is intended for temporary memberships.
* @param channelid Channel ID of channel to add to. See {@link Channel.id}.
Expand Down Expand Up @@ -723,37 +728,37 @@ module MumbleServer
* @param info Information about new user. Must include at least "name".
* @return The ID of the user. See {@link RegisteredUser.userid}.
*/
int registerUser(UserInfoMap info) throws ServerBootedException, InvalidUserException, InvalidSecretException;
int registerUser(UserInfoMap info) throws ServerBootedException, InvalidUserException, InvalidSecretException, ReadOnlyModeException;

/** Remove a user registration.
* @param userid ID of registered user. See {@link RegisteredUser.userid}.
*/
void unregisterUser(int userid) throws ServerBootedException, InvalidUserException, InvalidSecretException;
void unregisterUser(int userid) throws ServerBootedException, InvalidUserException, InvalidSecretException, ReadOnlyModeException;

/** Update the registration for a user. You can use this to set the email or password of a user,
* and can also use it to change the user's name.
* @param registration Updated registration record.
*/
idempotent void updateRegistration(int userid, UserInfoMap info) throws ServerBootedException, InvalidUserException, InvalidSecretException;
idempotent void updateRegistration(int userid, UserInfoMap info) throws ServerBootedException, InvalidUserException, InvalidSecretException, ReadOnlyModeException;

/** Fetch registration for a single user.
* @param userid ID of registered user. See {@link RegisteredUser.userid}.
* @return Registration record.
*/
idempotent UserInfoMap getRegistration(int userid) throws ServerBootedException, InvalidUserException, InvalidSecretException;
idempotent UserInfoMap getRegistration(int userid) throws ServerBootedException, InvalidUserException, InvalidSecretException, ReadOnlyModeException;

/** Fetch a group of registered users.
* @param filter Substring of user name. If blank, will retrieve all registered users.
* @return List of registration records.
*/
idempotent NameMap getRegisteredUsers(string filter) throws ServerBootedException, InvalidSecretException;
idempotent NameMap getRegisteredUsers(string filter) throws ServerBootedException, InvalidSecretException, ReadOnlyModeException;

/** Verify the password of a user. You can use this to verify a user's credentials.
* @param name User name. See {@link RegisteredUser.name}.
* @param pw User password.
* @return User ID of registered user (See {@link RegisteredUser.userid}), -1 for failed authentication or -2 for unknown usernames.
*/
idempotent int verifyPassword(string name, string pw) throws ServerBootedException, InvalidSecretException;
idempotent int verifyPassword(string name, string pw) throws ServerBootedException, InvalidSecretException, ReadOnlyModeException;

/** Fetch user texture. Textures are stored as zlib compress()ed 600x60 32-bit BGRA data.
* @param userid ID of registered user. See {@link RegisteredUser.userid}.
Expand All @@ -765,7 +770,7 @@ module MumbleServer
* @param userid ID of registered user. See {@link RegisteredUser.userid}.
* @param tex Texture (as a Byte-Array) to set for the user, or an empty texture to remove the existing texture.
*/
idempotent void setTexture(int userid, Texture tex) throws ServerBootedException, InvalidUserException, InvalidTextureException, InvalidSecretException;
idempotent void setTexture(int userid, Texture tex) throws ServerBootedException, InvalidUserException, InvalidTextureException, InvalidSecretException, ReadOnlyModeException;

/** Get virtual server uptime.
* @return Uptime of the virtual server in seconds
Expand All @@ -791,21 +796,21 @@ module MumbleServer
* - The certificate and/or private key do not contain RSA keys.
* - The certificate is not usable with the given private key.
*/
idempotent void updateCertificate(string certificate, string privateKey, string passphrase) throws ServerBootedException, InvalidSecretException, InvalidInputDataException;
idempotent void updateCertificate(string certificate, string privateKey, string passphrase) throws ServerBootedException, InvalidSecretException, InvalidInputDataException, ReadOnlyModeException;

/**
* Makes the given user start listening to the given channel.
* @param userid The ID of the user
* @param channelid The ID of the channel
*/
idempotent void startListening(int userid, int channelid) throws ServerBootedException, InvalidUserException;
idempotent void startListening(int userid, int channelid) throws ServerBootedException, InvalidUserException, ReadOnlyModeException;

/**
* Makes the given user stop listening to the given channel.
* @param userid The ID of the user
* @param channelid The ID of the channel
*/
idempotent void stopListening(int userid, int channelid) throws ServerBootedException, InvalidUserException;
idempotent void stopListening(int userid, int channelid) throws ServerBootedException, InvalidUserException, ReadOnlyModeException;

/**
* @param userid The ID of the user
Expand Down Expand Up @@ -838,7 +843,7 @@ module MumbleServer
* @param channelid The ID of the channel
* @param userid The ID of the user
*/
idempotent void setListenerVolumeAdjustment(int channelid, int userid, float volumeAdjustment) throws ServerBootedException, InvalidSecretException, InvalidChannelException, InvalidUserException;
idempotent void setListenerVolumeAdjustment(int channelid, int userid, float volumeAdjustment) throws ServerBootedException, InvalidSecretException, InvalidChannelException, InvalidUserException, ReadOnlyModeException;

/**
* @param receiverUserIDs list of IDs of the users the message shall be sent to
Expand Down Expand Up @@ -937,5 +942,15 @@ module MumbleServer
* @return Checksum dict
*/
idempotent Ice::SliceChecksumDict getSliceChecksums();

/**
* @returns The state the underlying database is currently assumed to be in
*/
idempotent DBState getAssumedDatabaseState() throws InvalidSecretException;

/**
* Sets the assumed state of the underlying database
*/
idempotent void setAssumedDatabaseState(DBState state) throws InvalidSecretException, ReadOnlyModeException;
};
};
7 changes: 7 additions & 0 deletions src/murmur/MumbleServerI.h
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,13 @@ class MetaI : virtual public Meta {
virtual void getUptime_async(const ::MumbleServer::AMD_Meta_getUptimePtr &, const Ice::Current &);

virtual void getSlice_async(const ::MumbleServer::AMD_Meta_getSlicePtr &, const Ice::Current &);

virtual void getAssumedDatabaseState_async(const ::MumbleServer::AMD_Meta_getAssumedDatabaseStatePtr &,
const ::Ice::Current &);

virtual void setAssumedDatabaseState_async(const ::MumbleServer::AMD_Meta_setAssumedDatabaseStatePtr &,
::MumbleServer::DBState state,
const ::Ice::Current & = ::Ice::Current());
};

} // namespace MumbleServer
Expand Down
Loading

0 comments on commit 6969661

Please sign in to comment.