From 46eea5403abccb0f224f1a7ba7709d5d25916b45 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Wed, 28 Nov 2018 16:05:27 -0500 Subject: [PATCH] Address all Host stylecop issues asside from complexity/maintainibilty --- src/Tgstation.Server.Host/.xml | 8470 +++++++++++++++++ .../Components/Byond/IByondInstaller.cs | 2 +- .../Components/Byond/IByondManager.cs | 1 + .../Chat/Commands/VersionCommand.cs | 2 +- .../Components/Chat/JsonTrackingContext.cs | 9 + .../Components/Chat/Providers/IrcProvider.cs | 4 + .../Components/Compiler/DmbFactory.cs | 2 +- .../Components/Compiler/DmbProvider.cs | 3 - .../Components/Compiler/IDmbFactory.cs | 1 - .../Compiler/TemporaryDmbProvider.cs | 1 + .../Components/EventConsumer.cs | 4 +- .../Components/EventType.cs | 10 + .../Components/IInstance.cs | 4 +- .../Components/Instance.cs | 80 +- .../Components/InstanceFactory.cs | 4 +- .../Components/InstanceManager.cs | 7 +- .../Components/Interop/ChatCommand.cs | 14 + .../Components/Interop/CommContext.cs | 4 +- .../Components/Interop/Constants.cs | 68 +- .../Components/Repository/IRepository.cs | 4 +- .../Components/Repository/Repository.cs | 45 +- .../Repository/RepositoryManager.cs | 5 + .../Components/StaticFiles/Configuration.cs | 29 +- .../Watchdog/ApiValidationStatus.cs | 5 + .../Watchdog/DeadSessionController.cs | 1 + .../Components/Watchdog/MonitorAction.cs | 6 +- .../Watchdog/MonitorActivationReason.cs | 6 + .../Watchdog/PosixNetworkPromptReaper.cs | 6 +- .../Components/Watchdog/RebootState.cs | 2 + .../Components/Watchdog/SessionController.cs | 40 +- .../Watchdog/SessionControllerFactory.cs | 29 +- .../Components/Watchdog/Watchdog.cs | 284 +- .../Watchdog/WindowsNetworkPromptReaper.cs | 14 +- .../Configuration/DatabaseType.cs | 2 + .../Configuration/FileLoggingConfiguration.cs | 3 +- .../Configuration/SetupWizardMode.cs | 3 + .../Controllers/AdministrationController.cs | 12 +- .../Controllers/ByondController.cs | 10 +- .../Controllers/ChatController.cs | 15 +- .../Controllers/ConfigurationController.cs | 1 + .../Controllers/DreamDaemonController.cs | 23 +- .../Controllers/DreamMakerController.cs | 3 +- .../Controllers/HomeController.cs | 13 +- .../Controllers/InstanceController.cs | 28 +- .../Controllers/InstanceUserController.cs | 4 +- .../Controllers/JobController.cs | 4 +- .../Controllers/RepositoryController.cs | 104 +- src/Tgstation.Server.Host/Core/Application.cs | 1 + src/Tgstation.Server.Host/Core/IJobManager.cs | 4 +- .../Core/IProcessExecutor.cs | 2 +- .../Core/ISetupWizard.cs | 4 +- src/Tgstation.Server.Host/Core/JobManager.cs | 1 + src/Tgstation.Server.Host/Core/Process.cs | 11 + .../IO/DefaultIOManager.cs | 3 + .../IO/IPostWriteHandler.cs | 3 + .../20180906135553_MSInitialCreate.cs | 1 + .../20180906143029_MYInitialCreate.cs | 1 + .../Models/RepositorySettings.cs | 2 +- src/Tgstation.Server.Host/NativeMethods.cs | 4 + .../Security/WindowsSystemIdentity.cs | 4 +- .../Security/WindowsSystemIdentityFactory.cs | 1 + .../Tgstation.Server.Host.csproj | 1 + 62 files changed, 9058 insertions(+), 376 deletions(-) create mode 100644 src/Tgstation.Server.Host/.xml diff --git a/src/Tgstation.Server.Host/.xml b/src/Tgstation.Server.Host/.xml new file mode 100644 index 0000000000..3a30704774 --- /dev/null +++ b/src/Tgstation.Server.Host/.xml @@ -0,0 +1,8470 @@ + + + + Tgstation.Server.Host + + + + + + + + + + + + + + + + + Construct a + + The value of + The value of + The value of + + + + + + + + + + + + + The path to the BYOND bin folder + + + + + The file in which we store the for installations + + + + + The file in which we store the for the active installation + + + + + + + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + Map of byond s to s that complete when they are installed + + + + + The for the + + + + + Converts a BYOND to a + + The to convert + The representation of + + + + Construct a + + The value of + The value of + The value of + The value of + + + + + + + Installs a BYOND if it isn't already + + The BYOND to install + The for the operation + A representing the running operation + + + + + + + + + + + + + + + + Represents usage of the two primary BYOND server executables + + + + + The of the locked executables + + + + + The path to the DreamDaemon executable + + + + + The path to the dm/DreamMaker executable + + + + + Call if, during a detach, this version should not be deleted + + + + + For downloading and installing BYOND extractions for a given system + + + + + Get the file name of the DreamDaemon executable + + + + + Get the file name of the DreamMaker executable + + + + + Download a given BYOND + + The of BYOND to download + The for the operation + A resulting in the s of the zipfile + + + + Does actions necessary to get an extracted BYOND installation working + + The path to the BYOND installation + The of BYOND being installed + The for the operation + + + + + Attempts to cleans the BYOND cache folder for the system + + The for the operation + A representing the running operation + + + + For managing the BYOND installation + + + + + The currently active BYOND version + + + + + The installed BYOND versions + + + + + Change the active BYOND version + + The new + The for the operation + + + + Lock the current installation's location and return a + + The BYOND required + The for the operation + A resulting in the requested + + + + for Posix systems + + + + + The URL format string for getting BYOND linux version {0}.{1} zipfile + + + + + Path to the BYOND cache + + + + + + + + + + + The for the + + + + + The for the + + + + + The for the + + + + + Construct a + + The value of + The value of + The value of + + + + + + + + + + + + + for windows systems + + + + + The URL format string for getting BYOND windows version {0}.{1} zipfile + + + + + Directory to byond installation configuration + + + + + BYOND's DreamDaemon config file + + + + + Setting to add to to suppress an invisible user prompt for running a trusted mode .dmb + + + + + The directory that contains the BYOND directx redistributable + + + + + + + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + If DirectX was installed + + + + + Construct a + + The value of + The value of + The value of + + + + + + + + + + + + + + + + Represents a channel + + + + + Backing field for . Represented as a to avoid BYOND percision loss + + + + + The channel Id. + + remaps this to an internal id using + + + + The user friendly name of the + + + + + The name of the connection the belongs to + + + + + If this is considered a channel for admin commands + + + + + If this is a 1-to-1 chat channel + + + + + For user use + + + + + Represents a mapping of a + + + + + The Id of the + + + + + The original + + + + + If is a watchdog channel + + + + + If the is an updates channel + + + + + The with the mapped Id + + + + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + Unchanging s in the mapped by + + + + + Map of s in use, keyed by + + + + + Map of s to s + + + + + The active s for the + + + + + The for + + + + + The initial for the + + + + + The for the + + + + + The that monitors incoming chat messages + + + + + The that completes when s change + + + + + Used for remapping s + + + + + If has been called + + + + + Construct a + + The value of + The value of + The value of + The to populate with + The value of + The value of + The value of + The used to populate + + + + + + + Remove a from and optionally updating the as well + + The of the to delete + If should be update + The for the operation + A resulting in the being removed if it exists, otherwise + + + + Processes a + + The who recevied + The to process + The for the operation + A representing the running operation + + + + Monitors active providers for new s + + The for the operation + A representing the running operation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + Construct a + + The value of + The value of + The value of + The value of + + + + + + + for decoding bools returned by BYOND + + + + + + + + + + + + + + For displaying the installed Byond version + + + + + + + + + + + + + + The for the + + + + + The for the + + + + + Construct a + + The value of + The value of + + + + + + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + Construct a + + The value of + The value of + The value of + The value of + The value of + + + + Set a for the + + The to set + + + + + + + Represents a command made from DM code + + + + + + + + + + + + + + The for the + + + + + Set a new + + The value of + + + + + + + Represents a command that can be invoked by talking to chat bots + + + + + The text to invoke the command. May not be "?" or "help" (case-insensitive) + + + + + The help text to display when queires are made about the command + + + + + If the command should only be available to s who's has set + + + + + Invoke the + + The text after with leading whitespace trimmed + The who invoked the command + The for the operation + A resulting in a to send to the invoker + + + + Factory for built in s + + + + + Generate builtin s + + A of s + + + + kek + + + + + kek + + + + + + + + + + + + + + + + + Command for reading the active s + + + + + + + + + + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + Construct a + + The value of + The value of + The value of + The value of + + + + + + + For displaying + + + + + + + + + + + + + + The for the + + + + + The for the + + + + + The for the + + + + + Construct a + + The value of + The value of + The value of + + + + + + + to return the + + + + + + + + + + + + + + The for the + + + + + Construct a + + + + + + + + + For managing connected chat services + + + + + If a given set of is connected + + The of the connection + if it is connected, otherwise + + + + Registers a to use + + A + + + + Change chat settings. If the is not currently in use, a new connection will be made instead + + The new + The for the operation + A representing the running operation. Will complete immediately if the property of is + + + + Disconnects and deletes a given connection + + The of the connection + The for the operation + A representing the running operation + + + + Change chat channels + + The of the connection + An of the new list of s + The for the operation + A representing the running operation + + + + Send a chat to a given set of + + The message being sent + The s of the s to send to + The for the operation + A representing the running operation + + + + Send a chat to configured watchdog channels + + The message being sent + The for the operation + A representing the running operation + + + + Send a chat to configured update channels + + The message being sent + The for the operation + A representing the running operation + + + + Start tracking json files for commands and channels + + The base path of the .jsons + The name of the chat channels json + The name of the chat commands json + The for the operation + A resulting in a tied to the lifetime of the json trackings + + + + For creating s + + + + + Create a + + The for the + The for the + The initial for the + A new + + + + Handles s that map to those defined in a + + + + + Handle a chat command + + The command name + Everything typed after minus leading spaces + The sending + The for the operation + A resulting in the response text to send back + + + + Represents a tracking of dynamic chat json files + + + + + If the should be used for + + + + + Read s from the + + The for the operation + A resulting in a of s in the + + + + Writes information about connected to the + + The s to write out + The for the operation + A representing the running operation + + + + Factory for s + + + + + Create a + + The containing settings for the new provider + A new + + + + + + + + + + + + + + + + + + + Represents a message recieved by a + + + + + The text of the message + + + + + The who sent the + + + + + for the Discord app + + + + + + + + + + + The for the + + + + + The for the + + + + + The token used for connecting to discord + + + + + of mapped s + + + + + Normalize a discord mention string + + The mention provided by the Discord library + The normalized mention + + + + Construct a + + The value of + The value of + + + + + + + Handle a message recieved from Discord + + The + A representing the running operation + + + + + + + + + + + + + + + + For interacting with a chat service + + + + + If the + + + + + The that indicates the was mentioned + + + + + Get a resulting in the next the recieves or on a disconnect + + The for the operation + A resulting in the next available + Note that private messages will come in the form of s not returned in + + + + Attempt to connect the + + The for the operation + A resulting in on success, otherwise + + + + Gracefully disconnects the provider. Implies a call to + + The for the operation + A representing the running operation + + + + Get the s for given + + The s to map + The for the operation + A resulting in a of the s representing + + + + Send a message to the + + The to send to + The message contents + The for the operation + A representing the running operation + + + + for internet relay chat + + + + + + + + + + + The for the + + + + + The for the + + + + + The client + + + + + Address of the server to connect to + + + + + Port of the server to connect to + + + + + IRC nickname + + + + + Password which will used for authentication + + + + + The of + + + + + Map of s to channel names + + + + + Map of s to query users + + + + + Id counter for + + + + + The used for + + + + + If we are disconnecting + + + + + Construct an + + The to get the from + The value of + The value of logger + The value of + The value of + The value of + The value of + The value of + If should be used + + + + + + + Handle an IRC message + + The + If this is a query message + + + + When a query message is received in IRC + + The sender of the event + The + + + + When a channel message is received in IRC + + The sender of the event + The + + + + + + + + + + + + + + + + + + + of received s + + + + + that completes while isn't empty + + + + + Construct a + + + + + + + + + + + Queues a for + + The to queue + + + + + + + + + + + + + + + + + + + + + + + + + The for the + + + + + The for the + + + + + The for the + + + + + Construct a + + The value of + The value of + The value of + + + + + + + Represents a chat message requested by DD + + + + + The message string + + + + + The list of internal channel ids to send to + + + + + Represents a tgs_chat_user datum + + + + + Backing field for . Represented as a to avoid BYOND percision loss + + + + + The internal user id + + + + + The friendly name of the user + + + + + The text to mention the user + + + + + The the user spoke from + + + + + Standard + + + + + + + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for + + + + + representing calls to + + + + + resulting in the latest yet to exist + + + + + The latest + + + + + Construct a + + The value of + The value of + The value of + The value of + + + + + + + Delete the of + + The to clean + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The for the + + + + + The for the + + + + + The to run when is called + + + + + Construct a + + The value of + The value of + The value of + + + + + + + + + + + + + Name of the primary directory used for compilation + + + + + Name of the secondary directory used for compilation + + + + + Extension for .dmbs + + + + + Extension for .dmes + + + + + The for + + + + + The for + + + + + The for + + + + + The for + + + + + The for + + + + + The for + + + + + The for + + + + + The for + + + + + The for + + + + + The for + + + + + The for + + + + + If a compile job is running + + + + + Construct + + The value of + The value of + The value of + The value of + The value of + The value of + The value of + The value of + The value of + The value of + The value of + + + + Run a quick DD instance to test the DMAPI is installed on the target code + + The timeout in seconds for validation + The level to use to validate the API + The for the operation + The current + The port to use for API validation + The for the operation + A representing the running operation + + + + Compiles a .dme with DreamMaker + + The path to the DreamMaker executable + The for the operation + The for the operation + A representing the running operation + + + + Adds server side includes to the .dme being compiled + + The for the operation + The for the operation + A representing the running operation + + + + + + + Sink for s + + + + + Load a new into the + + The to load + The for the operation + A representing the running operation + + + + Factory for s + + + + + Get a that completes when the result of a call to will be different than the previous call if any + + A representing the running operation + + + + If will succeed + + + + + Gets the next + + The amount of locks to give the resulting . It's must be called this many times to properly clean the job + A new + + + + Gets a for a given + + The to make the for + The for the operation + A resulting in a new representing the on success, on failure + + + + Deletes all compile jobs that are inactive in the Game folder + + An optional compile job to not delete + The for the operation + A representing the running operation + + + + For managing the compiler + + + + + Starts a compile + + The being compiled from the + The for the compile + The time in seconds to wait while validating the API + The to copy from + The to report compilation progress + The estimated amount of time the compile will take + The for the operation + A resulting in the partially populated for the operation. In particular, note the field will only have it's field populated + + + + Temporary + + + + + + + + + + + + + + + + + Construct a + + The value of + The value of + The value of + + + + + + + + + + The for the + + + + + The for the + + + + + Construct an + + The value of + + + + + + + Set the for the + + The value of + + + + Types of events. Mirror in tgs.dm + + + + + Parameters: Reference name, commit sha + + + + + Parameters: Checkout target + + + + + No parameters + + + + + Parameters: Pull request number, pull request sha, merger message + + + + + Parameters: Absolute path to repository root + + + + + Parameters: Version being installed + + + + + Parameters: Error string + + + + + Parameters: Old active version, new active version + + + + + Parameters: Game directory path, origin commit sha + + + + + No parameters + + + + + Parameters: Game directory path, "1" if compile succeeded and api validation failed, "0" otherwise + + + + + Parameters: Game directory path + + + + + No parameters + + + + + Parameters: Base sha, target sha, base reference, target reference + + + + + Provides absolute paths to the latest compiled .dmbs + + + + + The file name of the .dmb + + + + + The primary game directory with a trailing directory separator + + + + + The secondary game directory with a trailing directory separator + + + + + The of the .dmb + + + + + Disposing the won't cause a cleanup of the working directory + + + + + Consumes s and takes the appropriate actions + + + + + Handle a given + + The + The parameters for + The for the operation + A resulting in if more should run, otherwise + + + + For interacting with the instance services + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The latest staged + + The latest if it exists + + + + Rename the + + The new name for the + + + + Change the for the + + The new auto update inteval + A representing the running operation + + + + Run the compile job and insert it into the database. Meant to be called by a + + The running + The for the operation + The to report compilation progress + The for the operation + A representing the running operation + + + + Factory for creating s + + + + + Create an + + The + A new + + + + For managing s + + + + + Get the associated with given + + The of the desired + The associated with the given + + + + Online an + + The of the desired + The for the operation + A representing the running operation + + + + Offline an + + The of the desired + The performing the operation + The for the operation + A representing the running operation + + + + Move an + + The of the desired + The new path of the . will have this set on if the operation completes successfully + The for the operation + A representing the running operation + + + + + + + + + + + + + + + + + + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The auto update + + + + + for + + + + + Construct an + + The value of + The value of + The value of + The value of + The value of + The value of + The value of + The value of + The value of + The value of + The value of + The value of + The value of + The value of + + + + + + + + + + Pull the repository and compile for every set of given + + How many minutes the operation should repeat. Does not include running time + The for the operation + A representing the running operation + + + + + + + + + + + + + + + + + + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + Construct an + + The value of + The value of + The value of + The value of + The value of + The value of + The value of + The value of + The value of + The value of + The value of + The value of + The value of + The value of + The value of + The value of + The value of + The value of + + + + + + + + + + + + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + Map of s to respective s + + + + + Used in to determine if database downgrades must be made + + + + + If the has been d + + + + + Construct an + + The value of + The value of + The value of + The value of + The value of + The used to register the as a + The value of + + + + + + + + + + + + + + + + + + + + + + + + + + + + Represents a chat command to be handled by DD + + + + + The command name + + + + + The command params + + + + + The that sent the command + + + + + Represents a command from DD + + + + + The dictionary of the + + + + + The raw JSON of the + + + + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + If the has been disposed + + + + + Construct an + + The value of + The value of + The path to watch + The filter to watch for + + + + + + + Runs when the triggers + + The sender of the event + The + + + + + + + Constants used for communication with the DMAPI + + + + + Identifies a TGS execution. The server version + + + + + Path to the + + + + + The + + + + + Generic OK response + + + + + Change port + + + + + Change reboot mode + + + + + Chat command + + + + + Notify of an + + + + + Response to an interop export from DM + + + + + Set port command + + + + + API validation command + + + + + Server primed command + + + + + World reboot command + + + + + Terminate process command + + + + + Chat send command + + + + + Topic command parameter + + + + + Command data + + + + + For notifying DD of s + + + + + The + + + + + The event parameters + + + + + Represents a registration of an interop session + + + + + Register a with the + + The to register + + + + Handles s + + + + + Handle a + + The to handle + The for the operation + A representing the running operation + + + + Representation of the initial json passed to DreamDaemon + + + + + The code used by the server to authenticate command Topics + + + + + If DD should just respond if it's API is working and then exit + + + + + The of the owner at the time of launch + + + + + JSON file name that contains current active chat channel information + + + + + JSON file DD should write to with available chat commands + + + + + JSON file DD should write to to send commands to the server + + + + + The of the launch + + + + + The level of the launch + + + + + The s in the launch + + + + + Information used in for reattaching and interop + + + + + Path to the chat commands json file + + + + + Path to the chat channels json file + + + + + Path to the server commands json file + + + + + Construct an + + + + + Construct an from a + + An to copy + + + + This model mirrors /datum/tgs_revision_information/test_merge + + + + + The unix time of when the test merge was applied + + + + + The of the + + + + + Construct a + + The to build from + The value of + + + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + Construct a + + The value of + The value of + The value of + The value of + + + + + + + + + + + + + The for the + + + + + Construct a + + The value of + + + + + + + For generating s + + + + + Generate a from a given and + + The optional username to use in the + The optional password to use in the + A new + + + + Represents an on-disk git repository + + + + + If the was cloned from GitHub.com + + + + + The if this + + + + + The if this + + + + + If tracks an upstream branch + + + + + The SHA of the HEAD + + + + + The current reference the HEAD is using. This can be a branch or tag + + + + + The current origin remote the is using + + + + + Checks if a given is a sha + + The git object to check + The for the operation + A resulting in if is a sha, otherwise + + + + Checks out a given + + The sha or reference to checkout + to report 0-100 progress of the operation + The for the operation + A representing the running operation + + + + Attempt to merge a GitHub pull request into HEAD + + The of the pull request + The name of the merge committer + The e-mail of the merge committer + The username to fetch from the origin repository + The password to fetch from the origin repository + to report 0-100 progress of the operation + The for the operation + A resulting in a representing the merge result that is after a fast forward or up to date, on a non-fast-forward, on a conflict + + + + Fetch commits from the origin repository + + The username to fetch from the origin repository + The password to fetch from the origin repository + to report 0-100 progress of the operation + The for the operation + A representing the running operation + + + + Requires the current HEAD to be a tracked reference. Hard resets the reference to what it tracks on the origin repository + + to report 0-100 progress of the operation + The for the operation + A resulting in the SHA of the new HEAD + + + + Requires the current HEAD to be a reference. Hard resets the reference to the given sha + + The sha hash to reset to + to report 0-100 progress of the operation + The for the operation + A resulting in the SHA of the new HEAD + + + + Requires the current HEAD to be a tracked reference. Merges the reference to what it tracks on the origin repository + + The name of the merge committer + The e-mail of the merge committer + to report 0-100 progress of the operation + The for the operation + A resulting in a representing the merge result that is after a fast forward, on a merge or up to date, on a conflict + + + + Runs the synchronize event script and attempts to push any changes made to the if on a tracked branch + + The username to fetch from the origin repository + The password to fetch from the origin repository + The name of the potential committer + The e-mail of the potential committer + to report 0-100 progress of the operation + If the synchronizations should be made to the tracked reference as opposed to a temporary branch + The for the operation + A resulting in if commits were pushed to the tracked origin reference, otherwise + + + + Copies the current working directory to a given + + The path to copy repository contents to + The for the operation + A representing the running operation + + + + Factory for creating and loading s + + + + + If something is holding a lock on the repository + + + + + If a operation is in progress + + + + + Attempt to load the from the default location + + The for the operation + The loaded if it exists, otherwise + + + + Clone the repository at + + The of the remote repository to clone + The branch to clone + The username to clone from + The password to clone from + A function to report 0-100 progress of the clone + The for the operation + The newly cloned , if one already exists + + + + Delete the current repository + + The for the operation + A representing the running operation + + + + + + + Indication of a GitHub repository + + + + + Template error message for when tracking of the most recent origin commit fails + + + + + The branch name used for publishing testmerge commits + + + + + + + + + + + + + + + + + + + + + + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + to be taken when is called + + + + + Converts a given to a + + to report 0-100 progress of the operation + A based on + + + + Construct a + + The value of + The value of + The value of + The value of + The value of + The value if + + + + + + + Generate a standard set of + + to report 0-100 progress of the operation + The username for the + The password for the + The for the operation + A new set of + + + + Runs a blocking force checkout to + + The committish to checkout + Progress reporter + The for the operation + + + + + + + + + + + + + Force push the current repository HEAD to ; + + The username to fetch from the origin repository + The password to fetch from the origin repository + to report 0-100 progress of the operation + The for the operation + A representing the running operation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The created s + + + + + The for the + + + + + The for the + + + + + Used for controlling single access to the + + + + + Construct a + + The value of + The value of + The value of + The value of + The value of + The value of + + + + + + + + + + + + + + + + + + + Name of the ignore file in + + + + + The for + + + + + The for + + + + + The for + + + + + The for + + + + + The for + + + + + The for + + + + + The for + + + + + The for + + + + + Construct + + The value of + The value of + The value of + The value of + The value of + The value of + The value of + + + + + + + Get the proper path to + + The relative path to + + + + Ensures standard configuration directories exist + + The for the operation + A representing the running operation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + For managing the Configuration directory + + + + + Copies all files in the CodeModifications directory to + + The .dme file being compiled + Path to the destination folder + The for the operation + A resulting in the if any + + + + Symlinks all directories in the GameData directory to + + Path to the destination folder + The for the operation + A representing the running operation + + + + Get for all items in a given + + The relative path in the Configuration directory + The for the operation. If , the operation will be performed as the user of the + The for the operation + A resulting in the s for the items in the directory. and will both be + + + + Reads a given + + The relative path in the Configuration directory + The for the operation. If , the operation will be performed as the user of the + The for the operation + A resulting in the of the file + + + + Create an empty directory at + + The relative path in the Configuration directory + The for the operation. If , the operation will be performed as the user of the + The for the operation. Usage may result in partial writes + A resulting in if the directory already existed, otherwise + + + + Attempt to delete an empty directory at + + The path of the empty directory to delete + The for the operation. If , the operation will be performed as the user of the + The for the operation + if the directory was empty and deleted, otherwise + + + + Writes to a given + + The relative path in the Configuration directory + The for the operation. If , the operation will be performed as the user of the + The data to write. If , the file is deleted + The hash any existing file must match in order for the write to succeed + The for the operation. Usage may result in partial writes + A resulting in the updated or if the write failed due to conflicts + + + + Represents code modifications via configuration + + + + + If the target dme was completely overwitten + + + + + The #include line which should be added to the beginning of the .dme if any + + + + + The #include line which should be added to the end of the .dme if any + + + + + Construct + + The value of + The value of + The value of + + + + Status of DMAPI validation + + + + + The DMAPI never contacted the server for validation + + + + + The server was contacted for validation but it was never requested + + + + + The validation request was malformed + + + + + Valid API. The game must be run with a minimum security level of + + + + + Valid API. The game must be run with a security level of + + + + + Valid API. The game must be run with a minimum security level of + + + + + Implements a fake "dead" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + If the was d + + + + + Construct a + + The value of + + + + + + + + + + + + + + + + + + + + + + + + + + + + On Windows, DreamDaemon will show an unskippable prompt when using /world/proc/OpenPort(). This looks out for those prompts and immediately clicks "Yes" if the owning process has registered for it + + + + + Register a given for network prompt reaping + + The to register + + + + Handles saving and loading + + + + + Save some + + The to save + The for the operation + A representing the running operation + + + + Load a saved + + The for the operation + A resulting in the stored if any + + + + Handles communication with a DreamDaemon + + + + + A that completes when DreamDaemon starts pumping the windows message queue after loading a .dmb or when it crashes + + + + + If the of is being used + + + + + If the DreamDaemon instance sent a + + + + + The DMAPI + + + + + The being used + + + + + The current port DreamDaemon is listening on + + + + + If the port should be rotated off when the world reboots + + + + + The current + + + + + A that completes when the server calls /world/Reboot() + + + + + Releases the without terminating it. Also calls + + which can be used to create a new similar to this one + + + + Sends a command to DreamDaemon through /world/Topic() + + The sanitized command to send + The for the operation + A resulting in the result of /world/Topic() + + + + Causes the world to start listening on a + + The port to change to + The for the operation + A resulting in if the operation succeeded, otherwise + + + + Attempts to change the current to + + The new + The for the operation + A resulting in if the operation succeeded, otherwise + + + + Changes to without telling the DMAPI + + + + + Enables the reading of custom chat commands from the + + + + + Factory for s + + + + + Create a from a freshly launch DreamDaemon instance + + The to use. will be updated with the minumum required security level for the launch + The to use + The current if any + If the of should be used + If the of should be used + If the should only validate the DMAPI then exit + The for the operation + A resulting in a new + + + + Create a from an existing DreamDaemon instance + + The to use + The for the operation + A resulting in a new on success or on failure to reattach + + + + Creates a that appears to have started and died with exit code -1 + + The for the + A dead + + + + Runs and monitors the twin server controllers + + + + + If the watchdog is running + + + + + If the alpha server is the active server + + + + + The currently running on the server + + + + + The the active server is using + + + + + The to be applied + + + + + The of the active server + + + + + Start the + + The for the operation + A representing the running operation + + + + Changes the . If currently triggers a graceful restart + + The new . May be modified + The for the operation + A representing the running operation + + + + Restarts the watchdog + + If the restart will be delayed until a reboot is detected in the active server's DMAPI and this function will retrun immediately + The for the operation + A representing the running operation + + + + Stops the watchdog + + If the termination will be delayed until a reboot is detected in the active server's DMAPI and this function will return immediately + The for the operation + A representing the running operation + + + + Cancels pending graceful actions + + The for the operation + A representing the running operation + + + + For creating s + + + + + Creates a + + The for the + The for the with + The for the + The for the + The for the + The for the + The initial for the + A new + + + + Represents the result of trying to start a DD process + + + + + The time it took for to return. If the startup timed out + + + + + The if it exited + + + + + + + + The action for the monitor loop to take when control is returned to it + + + + + The monitor should continue as normal + + + + + Skips the next call to HandleMonitorWakeup action + + + + + The monitor should kill and restart both servers + + + + + The monitor should stop checking actions for this iteration and continue its loop + + + + + The monitor should exit. Does not kill servers + + + + + Reasons for the monitor to wake up + + + + + The active server crashed or exited + + + + + The inactive server crashed or exited + + + + + The active server called /world/Reboot() + + + + + The inactive server called /world/Reboot() + + + + + The inactive server is past that point where DD hangs when you press "Go" + + + + + A new .dmb was deployed + + + + + Server launch parameters were changed + + + + + The (absolute) state of the + + + + + If the inactive server is being rebooted + + + + + If the inactive server is in an unrecoverable state + + + + + The next to take in + + + + + The active + + + + + The inactive + + + + + + + + + + + Parameters necessary for duplicating a session + + + + + The used by DreamDaemon + + + + + Construct a + + + + + Construct a from a given and + + The to copy values from + The value of + + + + Represents the action to take when /world/Reboot() is called + + + + + Run DreamDaemon's normal reboot process + + + + + Shutdown DreamDaemon + + + + + Restart the DreamDaemon process + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The up to date + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The level the was launched with + + + + + The waits on when DreamDaemon currently has it's ports closed + + + + + The port to assign DreamDaemon when it queries for it + + + + + The that completes when DD tells us about a reboot + + + + + If we know DreamDaemon currently has it's port closed + + + + + If the has been disposed + + + + + The for the + + + + + If should be kept alive instead + + + + + Construct a + + The value of + The value of + The value of + The value of + The value of + The value of + The value of + The value of + The value of + The optional time to wait before failing the + + + + Finalizes an instance of the class. + + The finalizer dispose pattern is necessary so we don't accidentally leak the executable + + + + + + + Implements the pattern + + If this function was NOT called by the finalizer + + + + + + + Throws an if has been called + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + Change a given into the appropriate DreamDaemon command line word + + The level to change + A representation of the command line parameter + + + + Construct a + + The value of + The value of + The value of + The value of + The value of + The value of + The value of + The value of + The value of + The value of + The value of + + + + + + + + + + + + + + + + The time in seconds to wait from starting to start . Does not take responsiveness into account + + + + + + + + + + + + + + + + + + + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + If the should in + + + + + The for the monitor loop + + + + + The running the monitor loop + + + + + that completes when are changed and we are + + + + + Server designation alpha + + + + + Server designation bravo + + + + + If the servers should be released instead of shutdown + + + + + Construct a + + The value of + The value of + The value of + The value of + The value of + The value of + The value of + The value of + The to populate with + The value of + The value of + The initial value of . May be modified + The value of + The value of + + + + + + + Call on and and set them to + + + + + Implementation of . Does not lock + + If the termination will be delayed until a reboot is detected in the active server's DMAPI and this function will return immediately + If the termination will be announced using + The for the operation + + + + Handles the actions to take when the monitor has to "wake up" + + The that caused the invocation + The current . Will be modified upon retrn + The for the operation + A representing the running operation + + + + The loop that watches the watchdog + + The for the operation + A representing the running operation + + + + Stops . Doesn't kill the servers + + if the monitor was running, otherwise + + + + + + + Launches the + + If should be started by this function + If the launch should be announced to chat by this function + to use, if any + The for the operation + A representing the running operation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + Construct a + + The value of + The value of + The value of + The value of + The value of + The value of + + + + + + + Reattach information for a + + + + + for the Alpha session + + + + + for the Bravo session + + + + + Construct a + + + + + Construct a from a given with a given and + + The to copy information from + The used to build + The used to build + + + + + + + + + + Number of times to send the button click message. Should be at least 2 or it may fail to focus the window + + + + + Check for prompts each time this amount of milliseconds pass + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The list of s registered + + + + + The representing the lifetime of the + + + + + Construct a + + The value of + The value of + + + + + + + + + + + + + + + + Configuration options for the + + + + + The key for the the resides in + + + + + The to create + + + + + If the admin user should be enabled and have it's password reset + + + + + The connection string for the database + + + + + If the database should be deleted on application startup. Should not be used in production! + + + + + The form of the of a target MySQL/MariaDB server + + + + + Type of database to user + + + + + Use Microsoft SQL Server + + + + + Use MySQL + + + + + Use MariaDB + + + + + File logging configuration options + + + + + The key for the the resides in + + + + + Default value for + + + + + Default value for + + + + + Where log files are stored + + + + + If file logging is disabled + + + + + The minimum to display in logs + + + + + The minimum to display in logs for Microsoft library sources + + + + + General configuration options + + + + + The key for the the resides in + + + + + The default value for + + + + + The default value for + + + + + Minimum length of database user passwords + + + + + A GitHub personal access token to use for bypassing rate limits on requests. Requires no scopes + + + + + The + + + + + The timeout in milliseconds for sending and receiving topics to/from DreamDaemon. Note that a single topic exchange can take up to twice this value + + + + + Determines if the will run + + + + + Run the wizard if the appsettings.{Environment}.json is not present or empty + + + + + Force run the wizard + + + + + Only run the wizard and exit + + + + + Never run the wizard + + + + + Configuration for the automatic update system + + + + + The key for the the resides in + + + + + The tgstation/tgstation-server + + + + + The default value of + + + + + The default value of + + + + + The of the tgstation-server fork to recieve updates from + + + + + Prefix before the of TGS published in git tags + + + + + Asset package containing the new assembly in zip form + + + + + for + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + Construct an + + The for the + The for the + The value of + The value of + The value of + The value of + The value of + The for the + The containing value of + The containing value of + + + + + + + + + + Attempts to restart the server + + A resulting in the of the request + + + + A for API functions + + + + + The for the operation + + + + + The for the operation + + + + + The for the operation + + + + + The for the + + + + + The for the operation + + + + + If permissions are required to access the + + + + + Construct an + + The value of + The for the + The value of + The value of + + + + + + + Controller for managing s + + + + + The for the + + + + + The for the + + + + + Construct a + + The for the + The for the + The value of + The value of + The for the + + + + + + + + + + + + + for managing s + + + + + The for the + + + + + Construct a + + The for the + The for the + The value of + The for the + + + + Converts to a + + The + A based on + + + + + + + + + + + + + + + + + + + The for s + + + + + The for the + + + + + The for the + + + + + Construct a + + The for the + The for the + The value of + The value of + The for the + + + + If a should be returned from actions due to conflicts with one or both of the or the or a given tries to access parent directories + + The path to validate if any + The to use when calling into + if a should be returned, otherwise + + + + + + + Get the contents of a file at a + + The path of the file to get + The for the operation + A resulting in the for the operation + + + + Get the contents of a directory at a + + The path of the directory to get + The for the operation + A resulting in the for the operation + + + + + + + + + + Deletes an empty + + A representing the path to the directory to delete + The for the operation + A resulting in the of the operation + + + + for managing the + + + + + The for the + + + + + The for the + + + + + Construct a + + The for the + The for the + The value of + The value of + The for the + + + + + + + + + + Implementation of + + The to operate on if any + The for the operation + A resulting in the of the operation + + + + Stops DreamDaemon if it's running + + The for the operation + A resulting in the of the operation + + + + + + + Handle a HTTP PATCH to the + + The for the operation + A resulting in the of the request + + + + Controller for managing the compiler + + + + + The for the + + + + + The for the + + + + + Construct a + + The for the + The for the + The value of + The value of + The for the + + + + + + + + + + + + + + + + + + + Main for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + Construct a + + The for the + The for the + The value of + The value of + The value of + The value of + The value of + The for the + + + + Returns the version of the + + + + + + Attempt to authenticate a using + + The for the operation + A resulting in the of the operation + + + + Controller for managing s + + + + + File name to allow attaching instances + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + Construct a + + The for the + The for the + The value of + The value of + The value of + The value of + The value of + The for the + + + + + + + + + + + + + + + + + + + For managing s + + + + + Construct a + + The for the + The for the + The for the + + + + Checks a for errors + + The to check + A explaining any errors, if none + + + + + + + + + + + + + + + + + + + + + + for s + + + + + The for the + + + + + Construct a + + The for the + The for the + The value of + The for the + + + + + + + + + + + + + + + + An representing a + + The model being represented + + + + Construct a + + The for the + The for the + The for the + If the requires an + + + + Attempt to create a + + The being created + The for the operation + A resulting in the of the operation + + + + Attempt to read a + + The for the operation + A resulting in the of the operation + + + + Attempt to get a specific a + + The ID of the model to get + The for the operation + A resulting in the of the operation + + + + Attempt to update a + + The being updated + The for the operation + A resulting in the of the operation + + + + Attempt to delete a model with a particular + + The ID of the model to delete + The for the operation + A resulting in the of the operation + + + + Attempt to list entries of the + + The for the operation + A resulting in the of the operation + + + + Controller for managing the s + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + Construct a + + The for the + The for the + The value of + The value of + The value of + The for the + The containing value of + + + + + + + Delete the + + The for the operation + A resulting in the of the operation + + + + + + + + + + Helper for using the with the system + + + + + Construct a + + + + + Construct a for + + The rights required + + + + Construct a for + + The rights required + + + + Construct a for + + The rights required + + + + Construct a for + + The rights required + + + + Construct a for + + The rights required + + + + Construct a for + + The rights required + + + + Construct a for + + The rights required + + + + Construct a for + + The rights required + + + + Construct a for + + The rights required + + + + For managing s + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + Construct a + + The for the + The for the + The value of + The value of + The value of + The containing the value of + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The for the + + + + + The for the + + + + + The used for determining when the is + + + + + The for the + + + + + Construct an + + The value of + The value of + + + + Configure dependency injected services + + The to configure + + + + Configure the + + The to configure + The for the + The value of + The for the + + + + + + + Extensions for + + + + + Gets a from a given + + The to get the from + A new + + + + Return a for s + + The to configure + + + + Suppress TaskCanceledException warnings when a user aborts a request + + The to configure + + + + + + + + + + + + + The for the + + + + + Construct a + + The value of . Created scopes must be able to provide instances of + + + + + + + + + + + + + + + + The for the + + + + + Construct a + + The value of + + + + Create a + + A new + + + + + + + + + + Configures the ASP.NET Core web application + + + + + Prefix to + + + + + A more verbose version of + + + + + The version of the + + + + + Mark the as ready to run + + The that put the application in a corrupted state if any + + + + For waiting asynchronously + + + + + Create a that completes after a given + + The that must elapse + The for the operation + A representing the running operation + + + + Factory for scoping usage of s. Meant for use by + + + + + Run an in the scope of an + + The operation to run + A representing the running + + + + For creating + + + + + Create a + + The + The to create + A new + + + + For creating s + + + + + Create a client with anonymous authentication. Low rate limit + + A new + + + + Create a client with authentication using a personal access token + + The GitHub personal access token + A new + + + + Manages the runtime of s + + + + + Get the for a + + The to get for + The of + + + + Registers a given and begins running it + + The + The operation to run taking the started , a , progress reporter and a + The for the operation + A representing a running operation + + + + Wait for a given to complete + + The to wait for + The to cancel the + A that will cancel the + The for the operation + A representing the + + + + Cancels a give + + The to cancel + The who cancelled the + If the operation should wait until the job exits before completing + The for the operation + A resulting in if the was cancelled, if it couldn't be found + + + + For identifying the current platform + + + + + If the current platform is a Windows platform + + + + + The extension of executable script files for the system + + + + + Abstraction over a + + + + + The ' ID + + + + + The representing the time until the becomes "idle" + + + + + Get the stderr output of the + + The stderr output of the + + + + Get the stdout output of the + + The stdout output of the + + + + Get the stderr and stdout output of the + + The stderr and stdout output of the + + + + Terminates the process + + + + + Represents process lifetime + + + + + The resulting in the exit code of the process + + + + + Set's the owned to + + + + + For launching ' + + + + + Launch a + + The full path to the executable file + The working directory for the + The arguments for the + If standard output should be read + If standard error should be read + If shell execute should not be used. Ignored if or are set + A new + + + + Get a by + + The + The represented by on success, on failure + + + + Handler for server restarts + + + + + Handle a restart of the server + + The being updated to, if not being changed + The for the operation + A representing the running operation + + + + Represents the lifetime of a registration + + + + + Represents a service that may take an updated assembly and run it, stopping the current assembly in the process + + + + + if live updates are supported, . and will fail if this is + + + + + Run a new assembly and stop the current one. This will likely trigger all active s + + The the is updating to + The that points to the .zip file that contains the new assembly + The for the operation + if the update started successfully, if there was another update in progress + + + + Register a given to run before stopping the server for a restart + + The to register + A new representing the scope of the registration + + + + Restarts the + + A representing the running operation + + + + The command line setup wizard + + + + + Run the setup wizard if necessary + + The for the operation + A resulting in if the wizard ran, otherwise + + + + Operation exceptions thrown from the context of a + + + + + Construct a + + + + + Construct a with a + + The message for the + + + + Construct a with a and + + The message for the + The inner for the nase + + + + Class for pairing s with s + + + + + The for + + + + + The being run + + + + + Construct a + + A taking a and returning a that the will wrap + + + + + + + The progress of the job + + + + + Wait for to complete + + The for the operation + A representing the running operation + + + + Cancels + + + + + + + + The for the + + + + + The for the + + + + + of to running s + + + + + Construct a + + The value of + The value of + + + + + + + Gets the for a given if it exists + + The to get the for + The + + + + Runner for s + + The being run + The operation for the + The for the operation + A representing the running operation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Construct a + + + + + + + + + + + + + + + + + The for the + + + + + Construct a + + The value of + The value of + The value of + The value of + The value of + The value of + If was NOT just created + + + + + + + + + + + + + + + + + + + + + + + + + The for the + + + + + The for the + + + + + Create a resulting in the exit code of a given + + The to attach the for + A new resulting in the exit code of + + + + Construct a + + The value of + The value of + + + + + + + + + + + + + The + + + + + Construct a + + The value of + + + + + + + Async lock context helper + + + + + Asyncronously locks a + + The to lock + The for the operation + A resulting in the for the lock + + + + The locked + + + + + If has been called + + + + + Construct a + + The value of + + + + Finalizes an instance of the class. + + + + + Release the lock on + + + + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + Construct a + + The value of + The value of + The value of + The value of + The value of + The value of + The value of + The value of + The containing the value of + + + + A prompt for a yes or no value + + The question + The for the operation + A resulting in if the user replied yes, otherwise + + + + Prompts the user to enter the port to host TGS on + + The for the operation + A resulting in the hosting port, or to use the default + + + + Prompts the user to create a + + The for the operation + A resulting in the new + + + + Prompts the user to create a + + The for the operation + A resulting in the new + + + + Prompts the user to create a + + The for the operation + A resulting in the new + + + + Saves a given set to + + The file to save the to + The hosting port to save + The to save + The to save + The to save + The for the operation + A representing the running operation + + + + Runs the + + The path to the settings json to build + The for the operation + A representing the running operation + + + + + + + + + + + + + + + + + + + + + + that resolves paths to + + + + + Default buffer size used by .NET + + + + + Recursively empty a directory + + of the directory to empty + The for the operation + A representing the running operation + + + + Opens a for async writing at a given + + The path to open the at + A new ready for async writing + + + + Copies a directory from to + + The source directory path + The destination directory path + Files and folders to ignore at the root level + The for the operation + A of s representing the running operation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Abstraction for + + + + + If the is visible to the user + + + + + Write some to the + + The to write + If there should be a new line after the + The for the operation + A representing the running operation + + + + Wait for a key press on the + + The for the operations + A representing the running operation + + + + Read a line from the + + If the input should be retrieved using the '*' character + The for the operation + A resulting in the read by the + + + + Interface for using filesystems + + + + + Retrieve the full path of some given a relative path. Must be used before passing relative paths to other APIs. All other operations in this call this internally on given paths + + Some path to retrieve the full path of + as a full canonical path + + + + Gets the file name portion of a + + The path to get the file name of + The file name portion of + + + + Gets the file name portion of a with + + The path to get the file name of + The file name portion of + + + + Check if a contains the '..' parent directory accessor + + The path to check + if contains a '..' accessor, otherwise + + + + Copies a directory from to + + The source directory path + The destination directory path + Files and folders to ignore at the root level + The for the operation + A representing the running operation + + + + Check that the file at exists + + The file to check for existence + The for the operation + A resulting in if the file at exists, otherwise + + + + Check that the directory at exists + + The directory to check for existence + The for the operation + A resulting in if the directory at exists, otherwise + + + + Returns all the contents of a file at as a array + + The path of the file to read + A for the operation + A that results in the contents of a file at + + + + Returns directory names in a given + + The path to search for directories + The for the operation + A resulting in the directories in + + + + Returns file names in a given + + The path to search for files + The for the operation + A resulting in the files in + + + + Writes some to a file at overwriting previous content + + The path of the file to write + The contents of the file + A for the operation + A representing the running operation + + + + Copy a file from to + + The source file to copy + The destination path + A for the operation + A representing the running operation + + + + Gets the directory portion of a given + + A path to check + The directory portion of the given + + + + Gets a list of files in with the given + + The directory which contains the files + The extension to look for without the preceeding "." + A for the operation + A resulting in a list of paths to files in with the given + + + + Deletes a file at + + The path of the file to delete + A for the operation + A representing the running operation + + + + Create a directory at + + The path of the directory to create + A for the operation + A representing the running operation + + + + Recursively delete a directory + + The path to the directory to delete + A for the operation + A representing the running operation + + + + Combines an array of strings into a path + + The paths to combine + The combined path + + + + Moves a file at to + + The source file path + The destination path + A for the operation + A representing the running operation + + + + Moves a directory at to + + The source directory path + The destination path + A for the operation + A representing the running operation + + + + Downloads a file from + + The URL to download + A for the operation + A resulting in the s of the downloaded file + + + + Extract a set of to a given + + The path to unzip to + The s of the + The for the operation + A representing the running operation + + + + Handles changing file modes/permissions after writing + + + + + For handling system specific necessities after a write + + The full path to the file that was written + + + + For creating filesystem symbolic links + + + + + Create a symbolic link + + The path to the hard target + The path to the link + The for the operation + A representing the running operation + + + + For accessing the disk in a synchronous manner + + + + + Enumerate files in a given + + The path to look for files in + The for the operation + A of file names in + + + + Enumerate directories in a given + + The path to look for directories in + The for the operation + A of directory names in + + + + Create an empty directory at + + The path to create + The for the operation. Usage may result in partial writes + if the directory already existed, otherwise + + + + Read the s of a file at a given + + The path of the file to read + A array representing the contents of the file at + + + + Deletes a directory at if it's empty + + The path of the directory to delete + if the directory does not exist or is empty and was deleted. otherwise + + + + Write to a file at a given + + The path to the file to write + The new contents of the file + The function only succeeds if this parameter matches the SHA-1 hash of the contents of the current file. Contains the SHA1 of the file on disk once the function returns + The for the operation + on success, if the operation failed due to not matching the file's contents + + + + Checks if a given is a directory + + The path to check + if is a directory, otherwise + + + + for POSIX systems + + + + + + + + for posix systems + + + + + + + + An that resolve relative paths from another to a subdirectory of that + + + + + Path to the subdirectory attached to path resolutions + + + + + Construct a + + The that resolves to the directory to work out of + The value of + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + for Windows systems + + + + + + + + for windows systems + + + + + + + + Represents the host + + + + + If the should restart + + + + + Runs the + + The for the operation + A representing the running operation + + + + For creating s + + + + + Create a + + The arguments for the + The directory in which to install server updates + A new + + + + + + + The + + + + + The parent + + + + + See + + + + + Convert the to it's API form + + A new + + + + + + + The row Id + + + + + The + + + + + The + + + + + Convert the to it's API form + + A new + + + + + + + See + + + + + The of + + + + + See + + + + + The the was made with in string form + + + + + Convert the to it's API form + + A new + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The s in the + + + + + The s om the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + Construct a + + The for the + The containing the value of + The value of + The value of + + + + + + + + + + + + + If the MY_ class of migrations should be used instead of the MS_ class + + if the MY_ class of migrations should be used instead of the MS_ class, otherwise + + + + + + + + + + The for the + + + + + Construct a + + The value of + + + + Add a default admin to a given + + The to add an admin to + + + + + + + + + + + + + The row Id + + + + + The PID of a currently running DD instance + + + + + The access token used for communication with DD + + + + + The + + + + + The parent + + + + + + + + The row Id + + + + + The + + + + + The parent + + + + + Convert the to it's API form + + A new + + + + Represents the database + + + + + The s in the + + + + + The s in the + + + + + The s in the + + + + + The s in the + + + + + The s in the + + + + + The s in the + + + + + The in the + + + + + The in the + + + + + The in the + + + + + The in the + + + + + The in the + + + + + The for s + + + + + The for s + + + + + Saves changes made to the + + The for the operation + A representing the running operation + + + + Creates and migrates the + + The for the operation + A representing the running operation + + + + Attempt to downgrade the schema to the migration used for a given server + + The tgstation-server that the schema should downgrade for + The for the operation + A representing the running operation + + + + For initially seeding a database + + + + + Initially seed a given + + The to seed + The for the operation + A representing the running operation + + + + Changes the admin password in back to it's default and enables the account + + The to reset the admin password for + The for the operation + A representing the running operation + + + + Represents an in the database + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The for the + + + + + The s in the + + + + + The s for the + + + + + The s in the + + + + + The in the + + + + + Convert the to it's API form + + A new + + + + + + + The row Id + + + + + The of + + + + + The the belongs to + + + + + If the has any instance rights + + + + + Convert the to it's API form + + A new + + + + + + + See + + + + + See + + + + + The the job belongs to if any + + + + + A to run after the job completes. This will not affect the time, unless it is cancelled or errors + + This should only be used where there are database dependencies that also rely on the Job itself completing A.K.A. manually initiated s + + + + Convert the to it's API form + + A new + + + + The initial database migration for MSSQL + + + + + Applies the migration + + The to use + + + + Unapplies the migration + + The to use + + + + Builds the target model + + The to use + + + + The initial database migration for MySQL/MariaDB + + + + + Applies the migration + + The to use + + + + Unapplies the migration + + The to use + + + + Builds the target model + + The to use + + + + Add the and columns for MySQL/MariaDB + + + + + Applies the migration + + The to use + + + + Unapplies the migration + + The to use + + + + Builds the target model + + The to use + + + + Add the and columns for MSSQL + + + + + Applies the migration + + The to use + + + + Unapplies the migration + + The to use + + + + Builds the target model + + The to use + + + + Cleans up of nullable columns and foreign keys MySQL/MariaDB + + + + + Applies the migration + + The to use + + + + Unapplies the migration + + The to use + + + + Builds the target model + + The to use + + + + Cleans up of nullable columns and foreign keys MSSQL + + + + + Applies the migration + + The to use + + + + Unapplies the migration + + The to use + + + + Builds the target model + + The to use + + + + Marks the s of s as non-nullable for MSSQL + + + + + Applies the migration + + The to use + + + + Unapplies the migration + + The to use + + + + Builds the target model + + The to use + + + + Marks the s of s as non-nullable for MySQL/MariaDB + + + + + Applies the migration + + The to use + + + + Unapplies the migration + + The to use + + + + Builds the target model + + The to use + + + + Contains helpers for creating design time s + + + + + Path to the json file to use for migrations configuration + + + + + Path to the development json file to use for migrations configuration + + + + + Get the for the + + The for the + + + + + + + + + + + + + + + + for MySQL + + + + + Construct a + + The for the + The of for the + The for the + The for the + + + + + + + + + + Database representation of + + + + + The row Id + + + + + The for the + + + + + Base class for + + + + + Used to identify and authenticate the DreamDaemon instance + + + + + The system process ID + + + + + If the of the associated dmb is being used + + + + + The port DreamDaemon was last listening on + + + + + The current DreamDaemon reboot state + + + + + Construct a + + + + + Construct a from a given + + The to copy values from + + + + + + + + + + The row Id + + + + + The + + + + + The parent + + + + + Convert the to it's API form + + A new + + + + Many to many relationship for and + + + + + The row Id + + + + + The + + + + + The + + + + + + + + The row Id + + + + + The the belongs to + + + + + See + + + + + See + + + + + See s made from this + + + + + Convert the to it's API form + + A new + + + + for Sqlserver + + + + + Construct a + + The for the + The of for the + The for the + The for the + + + + + + + + + + + + + See + + + + + The initial the was merged with + + + + + Foreign key for + + + + + All the for the + + + + + Convert the to it's API form + + A new + + + + + + + The hash of the user's password + + + + + See + + + + + The uppercase invariant of + + + + + When was last changed + + + + + s created by this + + + + + The s for the + + + + + The s made by the + + + + + See + + If we should recurse on + If rights and system identifier should be shown + A new + + + + Convert the to it's API form + + If rights and system identifier should be shown + A new + + + + Database representation of + + + + + The row Id + + + + + The of the the belongs to + + + + + The for the Alpha server + + + + + The for the Bravo server + + + + + Base class for + + + + + If the Alpha session is the active session + + + + + Construct a + + + + + Construct a from a given + + The to copy values from + + + + Native methods used by the code + + + + + See https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-createsymboliclinka#parameters + + + + + See https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-getwindowthreadprocessid + + + + + See https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-findwindoww + + + + + See https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-sendmessage + + + + + See https://msdn.microsoft.com/en-us/library/ms633493(v=VS.85).aspx + + + + + See https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-enumchildwindows + + + + + See https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-getwindowtextw + + + + + See https://msdn.microsoft.com/en-us/library/windows/desktop/aa378184(v=vs.85).aspx + + + + + See https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-createsymboliclinkw + + + + + Entrypoint for the + + + + + The to use + + + + + Entrypoint for the + + The command line arguments + The + + + + Manages s for a scope + + + + + + + + + + + + + + Construct an empty + + + + + Construct an + + The value of + The value of + The value of + + + + + + + + + + + + + + + + The for the + + + + + The for the + + + + + The for the + + + + + Construct an + + The value of + The value of + The value of + + + + + + + + + + + + + The for the + + + + + The for the + + + + + Construct a + + The value of + The value of + + + + + + + + + + Length in s of generated base64 secure string + + + + + The for the + + + + + Construct a + + The value of + + + + + + + + + + + + + + + + Represents the currently authenticated + + + + + The authenticated user + + + + + The of if applicable + + + + + Get the value of a given + + The of the right to get + The value of . Note that if is all based rights will return 0 + + + + The of if applicable + + + + + For creating and accessing authentication contexts + + + + + The the created + + + + + Create an to populate + + The of the + The of the operation + The the resulting 's password must be valid after + The for the operation + A representing the running operation + + + + For injecting s that can look for + + + + + Setup the s for a given + + The containing the and of the request and the to add s to + The for the operation + A representing the running operation + + + + Contains various cryptographic functions + + + + + Generates a secure set of s + + The amount of s to generate + A secure set of s + + + + Sets a for a given + + The whos is to be set + The new password for the + If the is just being created + + + + Checks a given matches a given 's . This may result in being modified and this should be persisted + + The to check + The password to check + if matches the hash, otherwise + + + + Generates a 40-length secure ascii + + A 40-length secure ascii + + + + + + + The for the + + + + + The for the + + + + + The map of s to s + + + + + Construct an + + The value of + The value of + + + + + + + + + + + + + For keeping a specific alive for a period of time + + + + + The the manages + + + + + The for the + + + + + The to clean up + + + + + Construct an + + The value of + The used to delay the expiry + The to take on expiry + The + + + + + + + For caching s + + + + + Keep a 's alive until an time + + The the belongs to + The to cache + When the should expire + + + + Attempt to load a cached + + The the belongs to + The cached or if it doesn't exist or expired + + + + Represents a user on the current + + + + + A unique identifier for the user + + + + + The user's name + + + + + Clone the creating another copy that must have called on it + + A new mirroring the current one + + + + Runs a given in the context of the + + The to perform, should be simple and not use any s or threading + The for the operation + A representing the running operation + + + + Factory for s + + + + + Create a for a given + + The user to create a for + The for the operation + A new or if the has no + + + + Create a for a given username and password + + The username of the user + The password of the user + The for the operation + A new + + + + For creating s + + + + + The for the + + + + + Create a for a given + + The to create the token for. Must have the field available + The for the operation + A resulting in a new + + + + for posix systems + + TODO: Blocked by https://github.com/dotnet/corefx/issues/3187 + + + + + + + + + + + + + Amount of minutes until generated s expire + + + + + Amount of minutes to skew the clock for validation + + + + + Amount of bytes to use in the + + + + + + + + The for the + + + + + Construct a + + The value of + The used for generating the + + + + + + + for windows systems + + + + + + + + + + + The for the + + + + + The for the + + + + + Construct a using a + + The value of + + + + Construct a using a + + The value of + + + + + + + + + + + + + for windows systems. Uses long running tasks due to potential networked domains + + + + + The for the + + + + + Extract the username and domain name from a in the format "username\\domainname" + + The input + The output username + The output domain name. May be + + + + Construct a + + The value of logger + + + + + + + + + + + + + + + + + + + The for the + + + + + The s to run when the restarts + + + + + The absolute path to install updates to + + + + + The for the + + + + + The for the + + + + + If a server update has been or is being applied + + + + + Construct a + + The value of + The value of + + + + Throws an if the cannot be used + + If should be checked + + + + + + + + + + + + + + + + Implements + + The of any potential updates being applied + A representing the running operation + + + + + + + + + diff --git a/src/Tgstation.Server.Host/Components/Byond/IByondInstaller.cs b/src/Tgstation.Server.Host/Components/Byond/IByondInstaller.cs index b0526707b9..6b562f056e 100644 --- a/src/Tgstation.Server.Host/Components/Byond/IByondInstaller.cs +++ b/src/Tgstation.Server.Host/Components/Byond/IByondInstaller.cs @@ -33,7 +33,7 @@ namespace Tgstation.Server.Host.Components.Byond /// The path to the BYOND installation /// The of BYOND being installed /// The for the operation - /// + /// A representing the running operation Task InstallByond(string path, Version version, CancellationToken cancellationToken); /// diff --git a/src/Tgstation.Server.Host/Components/Byond/IByondManager.cs b/src/Tgstation.Server.Host/Components/Byond/IByondManager.cs index 767d15537e..7ace2266ac 100644 --- a/src/Tgstation.Server.Host/Components/Byond/IByondManager.cs +++ b/src/Tgstation.Server.Host/Components/Byond/IByondManager.cs @@ -26,6 +26,7 @@ namespace Tgstation.Server.Host.Components.Byond /// /// The new /// The for the operation + /// A representing the running operation Task ChangeVersion(Version version, CancellationToken cancellationToken); /// diff --git a/src/Tgstation.Server.Host/Components/Chat/Commands/VersionCommand.cs b/src/Tgstation.Server.Host/Components/Chat/Commands/VersionCommand.cs index 13b5a68f74..858d4bc8c0 100644 --- a/src/Tgstation.Server.Host/Components/Chat/Commands/VersionCommand.cs +++ b/src/Tgstation.Server.Host/Components/Chat/Commands/VersionCommand.cs @@ -27,7 +27,7 @@ namespace Tgstation.Server.Host.Components.Chat.Commands /// /// Construct a /// - /// + /// The value of public VersionCommand(IApplication application) { this.application = application ?? throw new ArgumentNullException(nameof(application)); diff --git a/src/Tgstation.Server.Host/Components/Chat/JsonTrackingContext.cs b/src/Tgstation.Server.Host/Components/Chat/JsonTrackingContext.cs index 00f8ade0a5..41646c7813 100644 --- a/src/Tgstation.Server.Host/Components/Chat/JsonTrackingContext.cs +++ b/src/Tgstation.Server.Host/Components/Chat/JsonTrackingContext.cs @@ -38,6 +38,15 @@ namespace Tgstation.Server.Host.Components.Chat bool active; + /// + /// Construct a + /// + /// The value of + /// The value of + /// The value of + /// The value of + /// The value of + /// The value of public JsonTrackingContext(IIOManager ioManager, ICustomCommandHandler customCommandHandler, ILogger logger, Action onDispose, string commandsPath, string channelsPath) { this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager)); diff --git a/src/Tgstation.Server.Host/Components/Chat/Providers/IrcProvider.cs b/src/Tgstation.Server.Host/Components/Chat/Providers/IrcProvider.cs index 8716ad3d0a..cce4b47d37 100644 --- a/src/Tgstation.Server.Host/Components/Chat/Providers/IrcProvider.cs +++ b/src/Tgstation.Server.Host/Components/Chat/Providers/IrcProvider.cs @@ -256,6 +256,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers client.WriteLine("CAP REQ :sasl", Priority.Critical); // needs to be put in the buffer before anything else cancellationToken.ThrowIfCancellationRequested(); } + client.Login(nickname, nickname, 0, nickname); } @@ -317,6 +318,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers if (client.GetIrcUser(nickname) == null) client.RfcNick(nickname); } + client.Listen(); }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current); } @@ -329,6 +331,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers logger.LogWarning("Unable to connect to IRC: {0}", e); return false; } + return true; }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current); @@ -397,6 +400,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers id = channelIdCounter++; channelIdMap.Add(id.Value, x.IrcChannel); } + return new Channel { RealId = id.Value, diff --git a/src/Tgstation.Server.Host/Components/Compiler/DmbFactory.cs b/src/Tgstation.Server.Host/Components/Compiler/DmbFactory.cs index 3a30381407..a95ac591c6 100644 --- a/src/Tgstation.Server.Host/Components/Compiler/DmbFactory.cs +++ b/src/Tgstation.Server.Host/Components/Compiler/DmbFactory.cs @@ -105,7 +105,7 @@ namespace Tgstation.Server.Host.Components.Compiler var deleteJob = ioManager.DeleteDirectory(job.DirectoryName.ToString(), cleanupCts.Token); Task otherTask; - // lock (this) //already locked below + // lock (this) //already locked below otherTask = cleanupTask; await Task.WhenAll(otherTask, deleteJob).ConfigureAwait(false); } diff --git a/src/Tgstation.Server.Host/Components/Compiler/DmbProvider.cs b/src/Tgstation.Server.Host/Components/Compiler/DmbProvider.cs index 31cbba7d18..bca0d73fbf 100644 --- a/src/Tgstation.Server.Host/Components/Compiler/DmbProvider.cs +++ b/src/Tgstation.Server.Host/Components/Compiler/DmbProvider.cs @@ -16,9 +16,6 @@ namespace Tgstation.Server.Host.Components.Compiler /// public string SecondaryDirectory => ioManager.ResolvePath(ioManager.ConcatPath(CompileJob.DirectoryName.ToString(), DreamMaker.BDirectoryName)); - /// - public RevisionInformation RevisionInformation => CompileJob.RevisionInformation; - /// /// The for the /// diff --git a/src/Tgstation.Server.Host/Components/Compiler/IDmbFactory.cs b/src/Tgstation.Server.Host/Components/Compiler/IDmbFactory.cs index f06ebd066b..fc153bb22c 100644 --- a/src/Tgstation.Server.Host/Components/Compiler/IDmbFactory.cs +++ b/src/Tgstation.Server.Host/Components/Compiler/IDmbFactory.cs @@ -43,6 +43,5 @@ namespace Tgstation.Server.Host.Components.Compiler /// The for the operation /// A representing the running operation Task CleanUnusedCompileJobs(CompileJob exceptThisOne, CancellationToken cancellationToken); - } } diff --git a/src/Tgstation.Server.Host/Components/Compiler/TemporaryDmbProvider.cs b/src/Tgstation.Server.Host/Components/Compiler/TemporaryDmbProvider.cs index 3cd0962106..628f77eb0b 100644 --- a/src/Tgstation.Server.Host/Components/Compiler/TemporaryDmbProvider.cs +++ b/src/Tgstation.Server.Host/Components/Compiler/TemporaryDmbProvider.cs @@ -36,6 +36,7 @@ namespace Tgstation.Server.Host.Components.Compiler /// public void Dispose() { } + /// public void KeepAlive() => throw new NotSupportedException(); } } \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Components/EventConsumer.cs b/src/Tgstation.Server.Host/Components/EventConsumer.cs index e28d93051f..e204cbcf8f 100644 --- a/src/Tgstation.Server.Host/Components/EventConsumer.cs +++ b/src/Tgstation.Server.Host/Components/EventConsumer.cs @@ -16,7 +16,7 @@ namespace Tgstation.Server.Host.Components readonly IConfiguration configuration; /// - /// The for the + /// The for the /// IWatchdog watchdog; @@ -46,8 +46,10 @@ namespace Tgstation.Server.Host.Components /// The value of public void SetWatchdog(IWatchdog watchdog) { +#pragma warning disable IDE0016 // Use 'throw' expression if (watchdog == null) throw new ArgumentNullException(nameof(watchdog)); +#pragma warning restore IDE0016 // Use 'throw' expression if (this.watchdog != null) throw new InvalidOperationException("watchdog already set!"); this.watchdog = watchdog; diff --git a/src/Tgstation.Server.Host/Components/EventType.cs b/src/Tgstation.Server.Host/Components/EventType.cs index c9da72aa68..f711742dbe 100644 --- a/src/Tgstation.Server.Host/Components/EventType.cs +++ b/src/Tgstation.Server.Host/Components/EventType.cs @@ -9,18 +9,22 @@ /// Parameters: Reference name, commit sha /// RepoResetOrigin = 0, + /// /// Parameters: Checkout target /// RepoCheckout = 1, + /// /// No parameters /// RepoFetch = 2, + /// /// Parameters: Pull request number, pull request sha, merger message /// RepoMergePullRequest = 3, + /// /// Parameters: Absolute path to repository root /// @@ -30,26 +34,32 @@ /// Parameters: Version being installed /// ByondInstallStart = 5, + /// /// Parameters: Error string /// ByondInstallFail = 6, + /// /// Parameters: Old active version, new active version /// ByondActiveVersionChange = 7, + /// /// Parameters: Game directory path, origin commit sha /// CompileStart = 8, + /// /// No parameters /// CompileCancelled = 9, + /// /// Parameters: Game directory path, "1" if compile succeeded and api validation failed, "0" otherwise /// CompileFailure = 10, + /// /// Parameters: Game directory path /// diff --git a/src/Tgstation.Server.Host/Components/IInstance.cs b/src/Tgstation.Server.Host/Components/IInstance.cs index d90826f9b4..299dbe6fc0 100644 --- a/src/Tgstation.Server.Host/Components/IInstance.cs +++ b/src/Tgstation.Server.Host/Components/IInstance.cs @@ -25,7 +25,7 @@ namespace Tgstation.Server.Host.Components /// The for the /// IByondManager ByondManager { get; } - + /// /// The for the /// @@ -37,7 +37,7 @@ namespace Tgstation.Server.Host.Components IChat Chat { get; } /// - /// The for the + /// The for the /// IConfiguration Configuration { get; } diff --git a/src/Tgstation.Server.Host/Components/Instance.cs b/src/Tgstation.Server.Host/Components/Instance.cs index 18a50f6143..b3c289e4d5 100644 --- a/src/Tgstation.Server.Host/Components/Instance.cs +++ b/src/Tgstation.Server.Host/Components/Instance.cs @@ -27,9 +27,6 @@ namespace Tgstation.Server.Host.Components /// public IByondManager ByondManager { get; } - /// - public IDreamMaker DreamMaker { get; } - /// public IWatchdog Watchdog { get; } @@ -39,8 +36,15 @@ namespace Tgstation.Server.Host.Components /// public StaticFiles.IConfiguration Configuration { get; } - /// - public ICompileJobConsumer CompileJobConsumer { get; } + /// + /// The for the + /// + readonly IDreamMaker dreamMaker; + + /// + /// The for the + /// + readonly ICompileJobConsumer compileJobConsumer; /// /// The for the @@ -81,6 +85,7 @@ namespace Tgstation.Server.Host.Components /// The auto update /// Task timerTask; + /// /// for /// @@ -92,11 +97,11 @@ namespace Tgstation.Server.Host.Components /// The value of /// The value of /// The value of - /// The value of + /// The value of /// The value of /// The value of /// The value of - /// The value of + /// The value of /// The value of /// The value of /// The value of @@ -108,11 +113,11 @@ namespace Tgstation.Server.Host.Components this.metadata = metadata ?? throw new ArgumentNullException(nameof(metadata)); RepositoryManager = repositoryManager ?? throw new ArgumentNullException(nameof(repositoryManager)); ByondManager = byondManager ?? throw new ArgumentNullException(nameof(byondManager)); - DreamMaker = dreamMaker ?? throw new ArgumentNullException(nameof(dreamMaker)); + this.dreamMaker = dreamMaker ?? throw new ArgumentNullException(nameof(dreamMaker)); Watchdog = watchdog ?? throw new ArgumentNullException(nameof(watchdog)); Chat = chat ?? throw new ArgumentNullException(nameof(chat)); Configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); - CompileJobConsumer = compileJobConsumer ?? throw new ArgumentNullException(nameof(compileJobConsumer)); + this.compileJobConsumer = compileJobConsumer ?? throw new ArgumentNullException(nameof(compileJobConsumer)); this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory)); this.dmbFactory = dmbFactory ?? throw new ArgumentNullException(nameof(dmbFactory)); this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager)); @@ -125,7 +130,7 @@ namespace Tgstation.Server.Host.Components public void Dispose() { timerCts?.Dispose(); - CompileJobConsumer.Dispose(); + compileJobConsumer.Dispose(); Configuration.Dispose(); Chat.Dispose(); Watchdog.Dispose(); @@ -135,9 +140,10 @@ namespace Tgstation.Server.Host.Components /// public async Task CompileProcess(Job job, IDatabaseContext databaseContext, Action progressReporter, CancellationToken cancellationToken) { - //DO NOT FOLLOW THE SUGGESTION FOR A THROW EXPRESSION HERE +#pragma warning disable IDE0016 // Use 'throw' expression if (job == null) throw new ArgumentNullException(nameof(job)); +#pragma warning restore IDE0016 // Use 'throw' expression if (databaseContext == null) throw new ArgumentNullException(nameof(databaseContext)); if (progressReporter == null) @@ -151,8 +157,6 @@ namespace Tgstation.Server.Host.Components var compileJobsTask = databaseContext.CompileJobs .Where(x => x.Job.Instance.Id == metadata.Id) .OrderByDescending(x => x.Job.StoppedAt) - //TODO: Replace with this select when the issues linked in https://github.com/tgstation/tgstation-server/issues/737 are fixed - //.Select(x => x.Job.StoppedAt.Value - x.Job.StartedAt.Value) .Select(x => new Job { StoppedAt = x.Job.StoppedAt, @@ -206,7 +210,7 @@ namespace Tgstation.Server.Host.Components logger.LogWarning(Repository.Repository.OriginTrackingErrorTemplate, repoSha); databaseContext.Instances.Attach(revInfo.Instance); } - + TimeSpan? averageSpan = null; var previousCompileJobs = await compileJobsTask.ConfigureAwait(false); if(previousCompileJobs.Count != 0) @@ -217,14 +221,14 @@ namespace Tgstation.Server.Host.Components averageSpan = totalSpan / previousCompileJobs.Count; } - compileJob = await DreamMaker.Compile(revInfo, dreamMakerSettings, ddSettings.StartupTimeout.Value, repo, progressReporter, averageSpan, cancellationToken).ConfigureAwait(false); + compileJob = await dreamMaker.Compile(revInfo, dreamMakerSettings, ddSettings.StartupTimeout.Value, repo, progressReporter, averageSpan, cancellationToken).ConfigureAwait(false); } compileJob.Job = job; - databaseContext.CompileJobs.Add(compileJob); //will be saved by job context - - job.PostComplete = ct => CompileJobConsumer.LoadCompileJob(compileJob, ct); + databaseContext.CompileJobs.Add(compileJob); // will be saved by job context + + job.PostComplete = ct => compileJobConsumer.LoadCompileJob(compileJob, ct); if (repositorySettingsTask != null) { @@ -234,9 +238,9 @@ namespace Tgstation.Server.Host.Components if (repositorySettings.AccessToken != null) { - //potential for commenting on a test merge change + // potential for commenting on a test merge change var outgoingCompileJob = LatestCompileJob(); - + if(outgoingCompileJob != null && outgoingCompileJob.RevisionInformation.CommitSha != compileJob.RevisionInformation.CommitSha) { var gitHubClient = gitHubClientFactory.CreateClient(repositorySettings.AccessToken); @@ -263,10 +267,9 @@ namespace Tgstation.Server.Host.Components updated ? "Updated" : "Deployed", metadata.Name, compileJob.RevisionInformation.OriginCommitSha, - compileJob.RevisionInformation.CommitSha - ); + compileJob.RevisionInformation.CommitSha); - //added prs + // added prs foreach (var I in compileJob .RevisionInformation .ActiveTestMerges @@ -277,7 +280,7 @@ namespace Tgstation.Server.Host.Components .Any(y => y.TestMerge.Number == x.Number))) tasks.Add(CommentOnPR(I.Number.Value, FormatTestMerge(I, false))); - //removed prs + // removed prs foreach (var I in outgoingCompileJob .RevisionInformation .ActiveTestMerges @@ -288,7 +291,7 @@ namespace Tgstation.Server.Host.Components .Any(y => y.TestMerge.Number == x.Number))) tasks.Add(CommentOnPR(I.Number.Value, "#### Test Merge Removed")); - //updated prs + // updated prs foreach(var I in compileJob .RevisionInformation .ActiveTestMerges @@ -341,11 +344,10 @@ namespace Tgstation.Server.Host.Components { var repositorySettingsTask = databaseContext.RepositorySettings.Where(x => x.InstanceId == metadata.Id).FirstAsync(jobCancellationToken); - //assume 5 steps with synchronize + // assume 5 steps with synchronize const int ProgressSections = 7; const int ProgressStep = 100 / ProgressSections; - const int NumSteps = 3; var doneSteps = 0; @@ -353,8 +355,8 @@ namespace Tgstation.Server.Host.Components { var tmpDoneSteps = doneSteps; ++doneSteps; - return progress => progressReporter((progress + 100 * tmpDoneSteps) / NumSteps); - }; + return progress => progressReporter((progress + (100 * tmpDoneSteps)) / NumSteps); + } using (var repo = await RepositoryManager.LoadRepository(jobCancellationToken).ConfigureAwait(false)) { @@ -374,7 +376,7 @@ namespace Tgstation.Server.Host.Components var repositorySettings = await repositorySettingsTask.ConfigureAwait(false); - //the main point of auto update is to pull the remote + // the main point of auto update is to pull the remote await repo.FetchOrigin(repositorySettings.AccessUser, repositorySettings.AccessToken, NextProgressReporter(), jobCancellationToken).ConfigureAwait(false); RevisionInformation currentRevInfo = null; @@ -415,7 +417,7 @@ namespace Tgstation.Server.Host.Components hasDbChanges = true; } - //take appropriate auto update actions + // take appropriate auto update actions bool shouldSyncTracked; if (repositorySettings.AutoUpdatesKeepTestMerges.Value) { @@ -459,7 +461,7 @@ namespace Tgstation.Server.Host.Components shouldSyncTracked = true; } - //synch if necessary + // synch if necessary if (repositorySettings.AutoUpdatesSynchronize.Value && startSha != repo.Head) { var pushedOrigin = await repo.Sychronize(repositorySettings.AccessUser, repositorySettings.AccessToken, repositorySettings.CommitterName, repositorySettings.CommitterEmail, NextProgressReporter(), shouldSyncTracked, jobCancellationToken).ConfigureAwait(false); @@ -498,7 +500,7 @@ namespace Tgstation.Server.Host.Components continue; } - //finally set up the job + // finally set up the job var compileProcessJob = new Job { StartedBy = user, @@ -527,9 +529,10 @@ namespace Tgstation.Server.Host.Components { break; } + logger.LogTrace("Leaving auto update loop..."); } - + /// public void Rename(string newName) { @@ -541,9 +544,9 @@ namespace Tgstation.Server.Host.Components /// public async Task StartAsync(CancellationToken cancellationToken) { - await Task.WhenAll(SetAutoUpdateInterval(metadata.AutoUpdateInterval.Value), Configuration.StartAsync(cancellationToken), ByondManager.StartAsync(cancellationToken), Chat.StartAsync(cancellationToken), CompileJobConsumer.StartAsync(cancellationToken)).ConfigureAwait(false); + await Task.WhenAll(SetAutoUpdateInterval(metadata.AutoUpdateInterval.Value), Configuration.StartAsync(cancellationToken), ByondManager.StartAsync(cancellationToken), Chat.StartAsync(cancellationToken), compileJobConsumer.StartAsync(cancellationToken)).ConfigureAwait(false); - //dependent on so many things, its just safer this way + // dependent on so many things, its just safer this way await Watchdog.StartAsync(cancellationToken).ConfigureAwait(false); CompileJob latestCompileJob = null; @@ -555,7 +558,7 @@ namespace Tgstation.Server.Host.Components } /// - public Task StopAsync(CancellationToken cancellationToken) => Task.WhenAll(SetAutoUpdateInterval(0), Configuration.StopAsync(cancellationToken), ByondManager.StopAsync(cancellationToken), Watchdog.StopAsync(cancellationToken), Chat.StopAsync(cancellationToken), CompileJobConsumer.StopAsync(cancellationToken)); + public Task StopAsync(CancellationToken cancellationToken) => Task.WhenAll(SetAutoUpdateInterval(0), Configuration.StopAsync(cancellationToken), ByondManager.StopAsync(cancellationToken), Watchdog.StopAsync(cancellationToken), Chat.StopAsync(cancellationToken), compileJobConsumer.StopAsync(cancellationToken)); /// public async Task SetAutoUpdateInterval(uint newInterval) @@ -571,12 +574,13 @@ namespace Tgstation.Server.Host.Components else toWait = Task.CompletedTask; } + await toWait.ConfigureAwait(false); if (newInterval == 0) return; lock (this) { - //race condition, just quit + // race condition, just quit if (timerTask != null) return; timerCts?.Dispose(); diff --git a/src/Tgstation.Server.Host/Components/InstanceFactory.cs b/src/Tgstation.Server.Host/Components/InstanceFactory.cs index b54cbc4d68..9841740c32 100644 --- a/src/Tgstation.Server.Host/Components/InstanceFactory.cs +++ b/src/Tgstation.Server.Host/Components/InstanceFactory.cs @@ -154,10 +154,10 @@ namespace Tgstation.Server.Host.Components /// public IInstance CreateInstance(Models.Instance metadata) { - //Create the ioManager for the instance + // Create the ioManager for the instance var instanceIoManager = new ResolvingIOManager(ioManager, metadata.Path); - //various other ioManagers + // various other ioManagers var repoIoManager = new ResolvingIOManager(instanceIoManager, "Repository"); var byondIOManager = new ResolvingIOManager(instanceIoManager, "Byond"); var gameIoManager = new ResolvingIOManager(instanceIoManager, "Game"); diff --git a/src/Tgstation.Server.Host/Components/InstanceManager.cs b/src/Tgstation.Server.Host/Components/InstanceManager.cs index b9b558cba2..bad12f9a14 100644 --- a/src/Tgstation.Server.Host/Components/InstanceManager.cs +++ b/src/Tgstation.Server.Host/Components/InstanceManager.cs @@ -94,6 +94,7 @@ namespace Tgstation.Server.Host.Components return; disposed = true; } + foreach (var I in instances) I.Value.Dispose(); } @@ -146,9 +147,10 @@ namespace Tgstation.Server.Host.Components throw new InvalidOperationException("Instance not online!"); instances.Remove(metadata.Id); } + try { - //we are the one responsible for cancelling his jobs + // we are the one responsible for cancelling his jobs var tasks = new List(); await databaseContextFactory.UseContext(async db => { @@ -194,6 +196,7 @@ namespace Tgstation.Server.Host.Components instance.Dispose(); throw; } + await instance.StartAsync(cancellationToken).ConfigureAwait(false); } @@ -236,7 +239,7 @@ namespace Tgstation.Server.Host.Components await Task.WhenAll(instances.Select(x => x.Value.StopAsync(cancellationToken))).ConfigureAwait(false); await instanceFactory.StopAsync(cancellationToken).ConfigureAwait(false); - //downgrade the db if necessary + // downgrade the db if necessary if (downgradeVersion != null) await databaseContextFactory.UseContext(db => db.SchemaDowngradeForServerVersion(downgradeVersion, cancellationToken)).ConfigureAwait(false); } diff --git a/src/Tgstation.Server.Host/Components/Interop/ChatCommand.cs b/src/Tgstation.Server.Host/Components/Interop/ChatCommand.cs index a8cc05feba..fbad20d56e 100644 --- a/src/Tgstation.Server.Host/Components/Interop/ChatCommand.cs +++ b/src/Tgstation.Server.Host/Components/Interop/ChatCommand.cs @@ -2,10 +2,24 @@ namespace Tgstation.Server.Host.Components.Interop { + /// + /// Represents a chat command to be handled by DD + /// sealed class ChatCommand { + /// + /// The command name + /// public string Command { get; set; } + + /// + /// The command params + /// public string Params { get; set; } + + /// + /// The that sent the command + /// public User User { get; set; } } } diff --git a/src/Tgstation.Server.Host/Components/Interop/CommContext.cs b/src/Tgstation.Server.Host/Components/Interop/CommContext.cs index e910ad7ffa..489053e91b 100644 --- a/src/Tgstation.Server.Host/Components/Interop/CommContext.cs +++ b/src/Tgstation.Server.Host/Components/Interop/CommContext.cs @@ -95,7 +95,7 @@ namespace Tgstation.Server.Host.Components.Interop /// /// The sender of the event /// The - async void HandleWrite(object sender, FileSystemEventArgs e) //this is what async void was made for + async void HandleWrite(object sender, FileSystemEventArgs e) // this is what async void was made for { try { @@ -115,7 +115,7 @@ namespace Tgstation.Server.Host.Components.Interop } catch (JsonException ex) { - //file not fully written yet + // file not fully written yet logger.LogDebug("Suppressing json convert exception for command file write: {0}", ex); return; } diff --git a/src/Tgstation.Server.Host/Components/Interop/Constants.cs b/src/Tgstation.Server.Host/Components/Interop/Constants.cs index 834a43063f..da5da51c91 100644 --- a/src/Tgstation.Server.Host/Components/Interop/Constants.cs +++ b/src/Tgstation.Server.Host/Components/Interop/Constants.cs @@ -1,31 +1,93 @@ namespace Tgstation.Server.Host.Components.Interop { + /// + /// Constants used for communication with the DMAPI + /// static class Constants { - //interop values, match them up with the appropriate api.dm - - //api version 4.0.0.0 + /// + /// Identifies a TGS execution. The server version + /// public const string DMParamHostVersion = "server_service_version"; + + /// + /// Path to the + /// public const string DMParamInfoJson = "tgs_json"; + /// + /// The + /// public const string DMInteropAccessIdentifier = "tgs_tok"; + /// + /// Generic OK response + /// public const string DMResponseSuccess = "tgs_succ"; + /// + /// Change port + /// public const string DMTopicChangePort = "tgs_port"; + + /// + /// Change reboot mode + /// public const string DMTopicChangeReboot = "tgs_rmode"; + + /// + /// Chat command + /// public const string DMTopicChatCommand = "tgs_chat_comm"; + + /// + /// Notify of an + /// public const string DMTopicEvent = "tgs_event"; + + /// + /// Response to an interop export from DM + /// public const string DMTopicInteropResponse = "tgs_interop"; + /// + /// Set port command + /// public const string DMCommandNewPort = "tgs_new_port"; + + /// + /// API validation command + /// public const string DMCommandApiValidate = "tgs_validate"; + + /// + /// Server primed command + /// public const string DMCommandServerPrimed = "tgs_prime"; + + /// + /// World reboot command + /// public const string DMCommandWorldReboot = "tgs_reboot"; + + /// + /// Terminate process command + /// public const string DMCommandEndProcess = "tgs_kill"; + + /// + /// Chat send command + /// public const string DMCommandChat = "tgs_chat_send"; + /// + /// Topic command parameter + /// public const string DMParameterCommand = "tgs_com"; + + /// + /// Command data + /// public const string DMParameterData = "tgs_data"; } } diff --git a/src/Tgstation.Server.Host/Components/Repository/IRepository.cs b/src/Tgstation.Server.Host/Components/Repository/IRepository.cs index e993508172..44cb5b39db 100644 --- a/src/Tgstation.Server.Host/Components/Repository/IRepository.cs +++ b/src/Tgstation.Server.Host/Components/Repository/IRepository.cs @@ -70,8 +70,8 @@ namespace Tgstation.Server.Host.Components.Repository /// The e-mail of the merge committer /// The username to fetch from the origin repository /// The password to fetch from the origin repository - /// The for the operation /// to report 0-100 progress of the operation + /// The for the operation /// A resulting in a representing the merge result that is after a fast forward or up to date, on a non-fast-forward, on a conflict Task AddTestMerge(TestMergeParameters testMergeParameters, string committerName, string committerEmail, string username, string password, Action progressReporter, CancellationToken cancellationToken); @@ -119,8 +119,8 @@ namespace Tgstation.Server.Host.Components.Repository /// The password to fetch from the origin repository /// The name of the potential committer /// The e-mail of the potential committer - /// If the synchronizations should be made to the tracked reference as opposed to a temporary branch /// to report 0-100 progress of the operation + /// If the synchronizations should be made to the tracked reference as opposed to a temporary branch /// The for the operation /// A resulting in if commits were pushed to the tracked origin reference, otherwise Task Sychronize(string username, string password, string committerName, string committerEmail, Action progressReporter, bool synchronizeTrackedBranch, CancellationToken cancellationToken); diff --git a/src/Tgstation.Server.Host/Components/Repository/Repository.cs b/src/Tgstation.Server.Host/Components/Repository/Repository.cs index d9d0daaa8d..fe4cd5c8df 100644 --- a/src/Tgstation.Server.Host/Components/Repository/Repository.cs +++ b/src/Tgstation.Server.Host/Components/Repository/Repository.cs @@ -84,27 +84,12 @@ namespace Tgstation.Server.Host.Components.Repository /// readonly Action onDispose; - void GetRepositoryOwnerName(string remote, out string owner, out string name) - { - //Assume standard gh format: [(git)|(https)]://github.com/owner/repo(.git)[0-1] - //Yes use .git twice in case it was weird - var toRemove = new string[] { ".git", "/", ".git" }; - foreach (string item in toRemove) - if (remote.EndsWith(item, StringComparison.OrdinalIgnoreCase)) - remote = remote.Substring(0, remote.LastIndexOf(item, StringComparison.OrdinalIgnoreCase)); - var splits = remote.Split('/'); - name = splits[splits.Length - 1]; - owner = splits[splits.Length - 2].Split('.')[0]; - - logger.LogTrace("GetRepositoryOwnerName({0}) => {1} / {2}", remote, owner, name); - } - /// /// Converts a given to a /// /// to report 0-100 progress of the operation /// A based on - static CheckoutProgressHandler CheckoutProgressHandler(Action progressReporter) => (a, completedSteps, totalSteps) => progressReporter((int)((((float)completedSteps) / totalSteps) * 100)); + static CheckoutProgressHandler CheckoutProgressHandler(Action progressReporter) => (a, completedSteps, totalSteps) => progressReporter((int)(((float)completedSteps) / totalSteps * 100)); /// /// Construct a @@ -140,6 +125,21 @@ namespace Tgstation.Server.Host.Components.Repository onDispose.Invoke(); } + void GetRepositoryOwnerName(string remote, out string owner, out string name) + { + // Assume standard gh format: [(git)|(https)]://github.com/owner/repo(.git)[0-1] + // Yes use .git twice in case it was weird + var toRemove = new string[] { ".git", "/", ".git" }; + foreach (string item in toRemove) + if (remote.EndsWith(item, StringComparison.OrdinalIgnoreCase)) + remote = remote.Substring(0, remote.LastIndexOf(item, StringComparison.OrdinalIgnoreCase)); + var splits = remote.Split('/'); + name = splits[splits.Length - 1]; + owner = splits[splits.Length - 2].Split('.')[0]; + + logger.LogTrace("GetRepositoryOwnerName({0}) => {1} / {2}", remote, owner, name); + } + /// /// Generate a standard set of /// @@ -264,7 +264,7 @@ namespace Tgstation.Server.Host.Components.Repository FailOnConflict = true, FastForwardStrategy = FastForwardStrategy.NoFastForward, SkipReuc = true, - OnCheckoutProgress = (a, completedSteps, totalSteps) => progressReporter(50 + ((int)((((float)completedSteps) / totalSteps) * 50))) + OnCheckoutProgress = (a, completedSteps, totalSteps) => progressReporter(50 + ((int)(((float)completedSteps) / totalSteps * 50))) }); } finally @@ -373,7 +373,7 @@ namespace Tgstation.Server.Host.Components.Repository try { var forcePushString = String.Format(CultureInfo.InvariantCulture, "+{0}:{0}", branch.CanonicalName); - repository.Network.Push(remote,forcePushString, GeneratePushOptions(progress => progressReporter((int)(0.9f * progress)), username, password, cancellationToken)); + repository.Network.Push(remote, forcePushString, GeneratePushOptions(progress => progressReporter((int)(0.9f * progress)), username, password, cancellationToken)); var removalString = String.Format(CultureInfo.InvariantCulture, ":{0}", branch.CanonicalName); repository.Network.Push(remote, removalString, GeneratePushOptions(progress => progressReporter(90 + (int)(0.1f * progress)), username, password, cancellationToken)); } @@ -510,6 +510,7 @@ namespace Tgstation.Server.Host.Components.Repository logger.LogTrace("Not synchronizing due to lack of credentials!"); return false; } + logger.LogTrace("Begin Synchronize..."); if (username == null) @@ -596,16 +597,18 @@ namespace Tgstation.Server.Host.Components.Repository /// public Task IsSha(string committish, CancellationToken cancellationToken) => Task.Factory.StartNew(() => { - //check if it's a tag + // check if it's a tag var gitObject = repository.Lookup(committish, ObjectType.Tag); if (gitObject != null) return false; cancellationToken.ThrowIfCancellationRequested(); - //check if it's a branch + + // check if it's a branch if (repository.Branches[committish] != null) return false; cancellationToken.ThrowIfCancellationRequested(); - //err on the side of references, if we can't look it up, assume its a reference + + // err on the side of references, if we can't look it up, assume its a reference if (repository.Lookup(committish) != null) return true; return false; diff --git a/src/Tgstation.Server.Host/Components/Repository/RepositoryManager.cs b/src/Tgstation.Server.Host/Components/Repository/RepositoryManager.cs index 2ec1c57468..4feeb46897 100644 --- a/src/Tgstation.Server.Host/Components/Repository/RepositoryManager.cs +++ b/src/Tgstation.Server.Host/Components/Repository/RepositoryManager.cs @@ -95,6 +95,7 @@ namespace Tgstation.Server.Host.Components.Repository throw new InvalidOperationException("The repository is already being cloned!"); CloneInProgress = true; } + try { using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false)) @@ -139,6 +140,7 @@ namespace Tgstation.Server.Host.Components.Repository { logger.LogDebug("Error deleting partially cloned repository! Exception: {0}", e); } + throw; } else @@ -147,12 +149,14 @@ namespace Tgstation.Server.Host.Components.Repository return null; } } + logger.LogInformation("Clone complete!"); } finally { CloneInProgress = false; } + return await LoadRepository(cancellationToken).ConfigureAwait(false); } @@ -188,6 +192,7 @@ namespace Tgstation.Server.Host.Components.Repository semaphore.Release(); return null; } + return new Repository(repo, ioManager, eventConsumer, credentialsProvider, repositoryLogger, () => { logger.LogTrace("Releasing semaphore due to Repository disposal..."); diff --git a/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs b/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs index f7a6436e22..4211761fed 100644 --- a/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs +++ b/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; -using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Text; using System.Threading; @@ -131,12 +130,11 @@ namespace Tgstation.Server.Host.Components.StaticFiles /// public async Task CopyDMFilesTo(string dmeFile, string destination, CancellationToken cancellationToken) { - using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false)) { await EnsureDirectories(cancellationToken).ConfigureAwait(false); - //just assume no other fs race conditions here + // just assume no other fs race conditions here var dmeExistsTask = ioManager.FileExists(ioManager.ConcatPath(CodeModificationsSubdirectory, dmeFile), cancellationToken); var headFileExistsTask = ioManager.FileExists(ioManager.ConcatPath(CodeModificationsSubdirectory, CodeModificationsHeadFile), cancellationToken); var tailFileExistsTask = ioManager.FileExists(ioManager.ConcatPath(CodeModificationsSubdirectory, CodeModificationsTailFile), cancellationToken); @@ -154,7 +152,7 @@ namespace Tgstation.Server.Host.Components.StaticFiles return null; string IncludeLine(string filePath) => String.Format(CultureInfo.InvariantCulture, "#include \"{0}\"", filePath); - + return new ServerSideModifications(headFileExistsTask.Result ? IncludeLine(CodeModificationsHeadFile) : null, tailFileExistsTask.Result ? IncludeLine(CodeModificationsTailFile) : null, false); } } @@ -168,7 +166,7 @@ namespace Tgstation.Server.Host.Components.StaticFiles configurationRelativePath = '.' + configurationRelativePath; var resolved = ioManager.ResolvePath(configurationRelativePath); var local = !nullOrEmptyCheck ? ioManager.ResolvePath(".") : null; - if (!nullOrEmptyCheck && resolved.Length < local.Length) //.. fuccbois + if (!nullOrEmptyCheck && resolved.Length < local.Length) // .. fuccbois throw new InvalidOperationException("Attempted to access file outside of configuration manager!"); return resolved; } @@ -201,6 +199,7 @@ namespace Tgstation.Server.Host.Components.StaticFiles result = null; return; } + enumerator = synchronousIOManager.GetFiles(path, cancellationToken); result.AddRange(enumerator.Select(x => new ConfigurationFile { @@ -236,7 +235,7 @@ namespace Tgstation.Server.Host.Components.StaticFiles #pragma warning disable CA5350 // Do not use insecure cryptographic algorithm SHA1. using (var sha1 = new SHA1Managed()) #pragma warning restore CA5350 // Do not use insecure cryptographic algorithm SHA1. - sha1String = String.Join("", sha1.ComputeHash(content).Select(b => b.ToString("x2", CultureInfo.InvariantCulture))); + sha1String = String.Join(String.Empty, sha1.ComputeHash(content).Select(b => b.ToString("x2", CultureInfo.InvariantCulture))); result = new ConfigurationFile { Content = content, @@ -248,7 +247,7 @@ namespace Tgstation.Server.Host.Components.StaticFiles } catch (UnauthorizedAccessException) { - //this happens on windows, dunno about linux + // this happens on windows, dunno about linux bool isDirectory; try { @@ -289,7 +288,7 @@ namespace Tgstation.Server.Host.Components.StaticFiles var results = new List { StaticIgnoreFile }; - //we don't want to lose trailing whitespace on linux + // we don't want to lose trailing whitespace on linux using (var reader = new StringReader(ignoreFileText)) { cancellationToken.ThrowIfCancellationRequested(); @@ -299,7 +298,7 @@ namespace Tgstation.Server.Host.Components.StaticFiles } return results; - }; + } IReadOnlyList ignoreFiles; @@ -316,9 +315,9 @@ namespace Tgstation.Server.Host.Components.StaticFiles { var fileName = ioManager.GetFileName(x); + // need to normalize bool ignored; if (platformIdentifier.IsWindows) - //need to normalize ignored = ignoreFiles.Any(y => fileName.ToUpperInvariant() == y.ToUpperInvariant()); else ignored = ignoreFiles.Any(y => fileName == y); @@ -367,7 +366,7 @@ namespace Tgstation.Server.Host.Components.StaticFiles if (!success) return; if (data != null) - postWriteHandler.HandleWrite(path); + postWriteHandler.HandleWrite(path); result = new ConfigurationFile { Content = data, @@ -379,7 +378,7 @@ namespace Tgstation.Server.Host.Components.StaticFiles } catch (UnauthorizedAccessException) { - //this happens on windows, dunno about linux + // this happens on windows, dunno about linux bool isDirectory; try { @@ -442,7 +441,7 @@ namespace Tgstation.Server.Host.Components.StaticFiles if (!EventTypeScriptFileNameMap.TryGetValue(eventType, out var scriptName)) return true; - //always execute in serial + // always execute in serial using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false)) { var files = await ioManager.GetFilesWithExtension(EventScriptsSubdirectory, platformIdentifier.ScriptFileExtension, cancellationToken).ConfigureAwait(false); @@ -458,6 +457,7 @@ namespace Tgstation.Server.Host.Components.StaticFiles return false; } } + return true; } @@ -466,7 +466,7 @@ namespace Tgstation.Server.Host.Components.StaticFiles { await EnsureDirectories(cancellationToken).ConfigureAwait(false); var path = ValidateConfigRelativePath(configurationRelativePath); - + var result = false; using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false)) { @@ -477,6 +477,7 @@ namespace Tgstation.Server.Host.Components.StaticFiles else CheckDeleteImpl(); } + return result; } } diff --git a/src/Tgstation.Server.Host/Components/Watchdog/ApiValidationStatus.cs b/src/Tgstation.Server.Host/Components/Watchdog/ApiValidationStatus.cs index 46e282802d..f830edb59e 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/ApiValidationStatus.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/ApiValidationStatus.cs @@ -9,22 +9,27 @@ /// The DMAPI never contacted the server for validation /// NeverValidated, + /// /// The server was contacted for validation but it was never requested /// UnaskedValidationRequest, + /// /// The validation request was malformed /// BadValidationRequest, + /// /// Valid API. The game must be run with a minimum security level of /// RequiresSafe, + /// /// Valid API. The game must be run with a security level of /// RequiresTrusted, + /// /// Valid API. The game must be run with a minimum security level of /// diff --git a/src/Tgstation.Server.Host/Components/Watchdog/DeadSessionController.cs b/src/Tgstation.Server.Host/Components/Watchdog/DeadSessionController.cs index c310f18d65..61a8a7cfc2 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/DeadSessionController.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/DeadSessionController.cs @@ -72,6 +72,7 @@ namespace Tgstation.Server.Host.Components.Watchdog return; disposed = true; } + Dmb.Dispose(); } diff --git a/src/Tgstation.Server.Host/Components/Watchdog/MonitorAction.cs b/src/Tgstation.Server.Host/Components/Watchdog/MonitorAction.cs index bf343bbced..306bdccc40 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/MonitorAction.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/MonitorAction.cs @@ -9,18 +9,22 @@ /// The monitor should continue as normal /// Continue, + /// - /// Skips the next call to HandleMonitorWakeup action + /// Skips the next call to HandleMonitorWakeup action /// Skip, + /// /// The monitor should kill and restart both servers /// Restart, + /// /// The monitor should stop checking actions for this iteration and continue its loop /// Break, + /// /// The monitor should exit. Does not kill servers /// diff --git a/src/Tgstation.Server.Host/Components/Watchdog/MonitorActivationReason.cs b/src/Tgstation.Server.Host/Components/Watchdog/MonitorActivationReason.cs index 8a3d842dca..2863028238 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/MonitorActivationReason.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/MonitorActivationReason.cs @@ -9,26 +9,32 @@ /// The active server crashed or exited /// ActiveServerCrashed, + /// /// The inactive server crashed or exited /// InactiveServerCrashed, + /// /// The active server called /world/Reboot() /// ActiveServerRebooted, + /// /// The inactive server called /world/Reboot() /// InactiveServerRebooted, + /// /// The inactive server is past that point where DD hangs when you press "Go" /// InactiveServerStartupComplete, + /// /// A new .dmb was deployed /// NewDmbAvailable, + /// /// Server launch parameters were changed /// diff --git a/src/Tgstation.Server.Host/Components/Watchdog/PosixNetworkPromptReaper.cs b/src/Tgstation.Server.Host/Components/Watchdog/PosixNetworkPromptReaper.cs index f12cfbc7b9..e69e8a360b 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/PosixNetworkPromptReaper.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/PosixNetworkPromptReaper.cs @@ -2,11 +2,13 @@ namespace Tgstation.Server.Host.Components.Watchdog { - //POSIX BYOND doesn't prompt you when you change the port /// sealed class PosixNetworkPromptReaper : INetworkPromptReaper { /// - public void RegisterProcess(IProcess process) { } + public void RegisterProcess(IProcess process) + { + // POSIX BYOND doesn't prompt you when you change the port + } } } diff --git a/src/Tgstation.Server.Host/Components/Watchdog/RebootState.cs b/src/Tgstation.Server.Host/Components/Watchdog/RebootState.cs index 70b4dac3f5..0dbc6e95aa 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/RebootState.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/RebootState.cs @@ -9,10 +9,12 @@ /// Run DreamDaemon's normal reboot process /// Normal = 0, + /// /// Shutdown DreamDaemon /// Shutdown = 1, + /// /// Restart the DreamDaemon process /// diff --git a/src/Tgstation.Server.Host/Components/Watchdog/SessionController.cs b/src/Tgstation.Server.Host/Components/Watchdog/SessionController.cs index d8fa568f03..6f6c6ac679 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/SessionController.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/SessionController.cs @@ -136,6 +136,7 @@ namespace Tgstation.Server.Host.Components.Watchdog /// The waits on when DreamDaemon currently has it's ports closed /// TaskCompletionSource portAssignmentTcs; + /// /// The port to assign DreamDaemon when it queries for it /// @@ -181,7 +182,7 @@ namespace Tgstation.Server.Host.Components.Watchdog /// The optional time to wait before failing the public SessionController(ReattachInformation reattachInformation, IProcess process, IByondExecutableLock byondLock, IByondTopicSender byondTopicSender, IJsonTrackingContext chatJsonTrackingContext, ICommContext interopContext, IChat chat, ILogger logger, DreamDaemonSecurity? launchSecurityLevel, uint? startupTimeout) { - this.chatJsonTrackingContext = chatJsonTrackingContext; //null valid + this.chatJsonTrackingContext = chatJsonTrackingContext; // null valid this.reattachInformation = reattachInformation ?? throw new ArgumentNullException(nameof(reattachInformation)); this.byondTopicSender = byondTopicSender ?? throw new ArgumentNullException(nameof(byondTopicSender)); this.process = process ?? throw new ArgumentNullException(nameof(process)); @@ -219,17 +220,18 @@ namespace Tgstation.Server.Host.Components.Watchdog StartupTime = process.Startup.IsCompleted ? (TimeSpan?)(DateTimeOffset.Now - startTime) : null }; return result; - }; + } + LaunchResult = GetLaunchResult(); logger.LogDebug("Created session controller. Primary: {0}, CommsKey: {1}, Port: {2}", IsPrimary, reattachInformation.AccessIdentifier, Port); } /// - /// Finalize the + /// Finalizes an instance of the class. /// /// The finalizer dispose pattern is necessary so we don't accidentally leak the executable -#pragma warning disable CA1821 // Remove empty Finalizers //TODO remove this when https://github.com/dotnet/roslyn-analyzers/issues/1241 is fixed +#pragma warning disable CA1821 // Remove empty Finalizers TODO: remove this when https://github.com/dotnet/roslyn-analyzers/issues/1241 is fixed ~SessionController() => Dispose(false); #pragma warning restore CA1821 // Remove empty Finalizers @@ -240,7 +242,10 @@ namespace Tgstation.Server.Host.Components.Watchdog GC.SuppressFinalize(this); } - /// + /// + /// Implements the pattern + /// + /// If this function was NOT called by the finalizer void Dispose(bool disposing) { lock (this) @@ -254,9 +259,10 @@ namespace Tgstation.Server.Host.Components.Watchdog process.Terminate(); byondLock.Dispose(); } + process.Dispose(); interopContext.Dispose(); - Dmb?.Dispose(); //will be null when released + Dmb?.Dispose(); // will be null when released chatJsonTrackingContext.Dispose(); disposed = true; } @@ -307,9 +313,10 @@ namespace Tgstation.Server.Host.Components.Watchdog logger.LogDebug("Exception while decoding chat message! Exception: {0}", e); goto default; } + break; case Constants.DMCommandServerPrimed: - //currently unused, maybe in the future + // currently unused, maybe in the future break; case Constants.DMCommandEndProcess: TerminationWasRequested = true; @@ -327,18 +334,17 @@ namespace Tgstation.Server.Host.Components.Watchdog } if (!nextPort.HasValue) - //not ready yet, so what we'll do is accept the random port DD opened on for now and change it later when we decide to - reattachInformation.Port = currentPort; + reattachInformation.Port = currentPort; // not ready yet, so what we'll do is accept the random port DD opened on for now and change it later when we decide to else { - //nextPort is ready, tell DD to switch to that - //if it fails it'll kill itself + // nextPort is ready, tell DD to switch to that + // if it fails it'll kill itself content = new Dictionary { { Constants.DMParameterData, nextPort.Value } }; reattachInformation.Port = nextPort.Value; overrideResponsePort = currentPort; nextPort = null; - //we'll also get here from SetPort so complete that task + // we'll also get here from SetPort so complete that task var tmpTcs = portAssignmentTcs; portAssignmentTcs = null; if (tmpTcs != null) @@ -347,6 +353,7 @@ namespace Tgstation.Server.Host.Components.Watchdog portClosedForReboot = false; } + break; case Constants.DMCommandApiValidate: if (!launchSecurityLevel.HasValue) @@ -356,6 +363,7 @@ namespace Tgstation.Server.Host.Components.Watchdog content = new ErrorMessage { Message = "Invalid API validation request!" }; break; } + if (!query.TryGetValue(Constants.DMParameterData, out var stringMinimumSecurityLevelObject) || !Enum.TryParse(stringMinimumSecurityLevelObject as string, out var minimumSecurityLevel)) apiValidationStatus = ApiValidationStatus.BadValidationRequest; else @@ -373,6 +381,7 @@ namespace Tgstation.Server.Host.Components.Watchdog default: throw new InvalidOperationException("Enum.TryParse failed to validate the DreamDaemonSecurity range!"); } + break; case Constants.DMCommandWorldReboot: if (ClosePortOnReboot) @@ -381,6 +390,7 @@ namespace Tgstation.Server.Host.Components.Watchdog content = new Dictionary { { Constants.DMParameterData, 0 } }; portClosedForReboot = true; } + var oldTcs = rebootTcs; rebootTcs = new TaskCompletionSource(); postRespond = () => oldTcs.SetResult(null); @@ -418,7 +428,8 @@ namespace Tgstation.Server.Host.Components.Watchdog public ReattachInformation Release() { CheckDisposed(); - //we still don't want to dispose the dmb yet, even though we're keeping it alive + + // we still don't want to dispose the dmb yet, even though we're keeping it alive var tmpProvider = reattachInformation.Dmb; reattachInformation.Dmb = null; released = true; @@ -441,8 +452,7 @@ namespace Tgstation.Server.Host.Components.Watchdog byondTopicSender.SanitizeString(Constants.DMInteropAccessIdentifier), byondTopicSender.SanitizeString(reattachInformation.AccessIdentifier), byondTopicSender.SanitizeString(Constants.DMParameterCommand), - //intentionally don't sanitize command, that's up to the caller - command); + command); // intentionally don't sanitize command, that's up to the caller var targetPort = overridePort ?? reattachInformation.Port; logger.LogTrace("Export to :{0}. Query: {1}", targetPort, commandString); diff --git a/src/Tgstation.Server.Host/Components/Watchdog/SessionControllerFactory.cs b/src/Tgstation.Server.Host/Components/Watchdog/SessionControllerFactory.cs index 4715f6443e..a4d57a73a5 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/SessionControllerFactory.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/SessionControllerFactory.cs @@ -5,7 +5,6 @@ using Newtonsoft.Json.Serialization; using System; using System.Globalization; using System.Linq; -using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -138,12 +137,12 @@ namespace Tgstation.Server.Host.Components.Watchdog const string JsonPostfix = "tgs.json"; var basePath = primaryDirectory ? dmbProvider.PrimaryDirectory : dmbProvider.SecondaryDirectory; - //delete all previous tgs json files - var files = await ioManager.GetFilesWithExtension(basePath, JsonPostfix, cancellationToken).ConfigureAwait(false); + // delete all previous tgs json files + var files = await ioManager.GetFilesWithExtension(basePath, JsonPostfix, cancellationToken).ConfigureAwait(false); await Task.WhenAll(files.Select(x => ioManager.DeleteFile(x, cancellationToken))).ConfigureAwait(false); - //i changed this back from guids, hopefully i don't regret that + // i changed this back from guids, hopefully i don't regret that string JsonFile(string name) => String.Format(CultureInfo.InvariantCulture, "{0}.{1}", name, JsonPostfix); var securityLevelToUse = launchParameters.SecurityLevel.Value; @@ -162,7 +161,7 @@ namespace Tgstation.Server.Host.Components.Watchdog throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Invalid DreamDaemonSecurity value: {0}", dmbProvider.CompileJob.MinimumSecurityLevel)); } - //setup interop files + // setup interop files var interopInfo = new JsonFile { AccessIdentifier = accessIdentifier, @@ -197,19 +196,19 @@ namespace Tgstation.Server.Host.Components.Watchdog var chatJsonTrackingContext = await chatJsonTrackingTask.ConfigureAwait(false); try { - //get the byond lock + // get the byond lock var byondLock = currentByondLock ?? await byond.UseExecutables(Version.Parse(dmbProvider.CompileJob.ByondVersion), cancellationToken).ConfigureAwait(false); try { - //create interop context + // create interop context var context = new CommContext(ioManager, loggerFactory.CreateLogger(), basePath, interopInfo.ServerCommandsJson); try { - //set command line options - //more sanitization here cause it uses the same scheme + // set command line options + // more sanitization here cause it uses the same scheme var parameters = String.Format(CultureInfo.InvariantCulture, "{2}={0}&{3}={1}", byondTopicSender.SanitizeString(application.Version.ToString()), byondTopicSender.SanitizeString(interopJsonFile), byondTopicSender.SanitizeString(Constants.DMParamHostVersion), byondTopicSender.SanitizeString(Constants.DMParamInfoJson)); - //important to run on all ports to allow port changing + // important to run on all ports to allow port changing var arguments = String.Format(CultureInfo.InvariantCulture, "{0} -port {1} -ports 1-65535 {2}-close -{3} -verbose -public -params \"{4}\"", dmbProvider.DmbName, primaryPort ? launchParameters.PrimaryPort : launchParameters.SecondaryPort, @@ -217,15 +216,16 @@ namespace Tgstation.Server.Host.Components.Watchdog SecurityWord(securityLevelToUse), parameters); - //See #719 + // See https://github.com/tgstation/tgstation-server/issues/719 var noShellExecute = !platformIdentifier.IsWindows; - //launch dd + + // launch dd var process = processExecutor.LaunchProcess(byondLock.DreamDaemonPath, basePath, arguments, noShellExecute: noShellExecute); try { networkPromptReaper.RegisterProcess(process); - //return the session controller for it + // return the session controller for it var result = new SessionController(new ReattachInformation { AccessIdentifier = accessIdentifier, @@ -238,7 +238,7 @@ namespace Tgstation.Server.Host.Components.Watchdog ServerCommandsJson = interopInfo.ServerCommandsJson, }, process, byondLock, byondTopicSender, chatJsonTrackingContext, context, chat, loggerFactory.CreateLogger(), launchParameters.SecurityLevel, launchParameters.StartupTimeout); - //writeback launch parameter's fixed security level + // writeback launch parameter's fixed security level launchParameters.SecurityLevel = securityLevelToUse; return result; @@ -317,6 +317,7 @@ namespace Tgstation.Server.Host.Components.Watchdog if (result == null) chatJsonTrackingContext.Dispose(); } + return result; } diff --git a/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs b/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs index 882a07170e..9fe9d2190b 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs @@ -5,6 +5,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Globalization; using System.Linq; using System.Text; @@ -134,6 +135,7 @@ namespace Tgstation.Server.Host.Components.Watchdog /// Server designation alpha /// ISessionController alphaServer; + /// /// Server designation bravo /// @@ -179,7 +181,7 @@ namespace Tgstation.Server.Host.Components.Watchdog if (serverControl == null) throw new ArgumentNullException(nameof(serverControl)); - + chat.RegisterCommandHandler(this); AlphaIsActive = true; @@ -239,7 +241,7 @@ namespace Tgstation.Server.Host.Components.Watchdog return; } - //merely set the reboot state + // merely set the reboot state var toKill = AlphaIsActive ? alphaServer : bravoServer; var other = AlphaIsActive ? bravoServer : alphaServer; if (toKill != null) @@ -257,26 +259,27 @@ namespace Tgstation.Server.Host.Components.Watchdog { logger.LogDebug("Monitor activation. Reason: {0}", activationReason); - //this is where the bulk of the watchdog handling code lives and is fraught with lambdas, sorry not sorry - //I'll do my best to walk you through it + // this is where the bulk of the watchdog handling code lives and is fraught with lambdas, sorry not sorry + // I'll do my best to walk you through it - //returns true if the inactive server can't be used immediately - //also sets monitor to restart if the above holds + // returns true if the inactive server can't be used immediately + // also sets monitor to restart if the above holds bool FullRestartDeadInactive() { if (monitorState.RebootingInactiveServer || monitorState.InactiveServerCritFail) { logger.LogInformation("Inactive server is {0}! Restarting monitor...", monitorState.InactiveServerCritFail ? "critically failed" : "still rebooting"); - monitorState.NextAction = MonitorAction.Restart; //will dispose server + monitorState.NextAction = MonitorAction.Restart; // will dispose server return true; } - return false; - }; - //trys to set inactive server's port to the public game port - //doesn't handle closing active server's port - //returns true on success and swaps inactiveserver and activeserver also sets LastLaunchParameters to ActiveLaunchParameters - //on failure, sets monitor to restart + return false; + } + + // trys to set inactive server's port to the public game port + // doesn't handle closing active server's port + // returns true on success and swaps inactiveserver and activeserver also sets LastLaunchParameters to ActiveLaunchParameters + // on failure, sets monitor to restart async Task MakeInactiveActive() { logger.LogDebug("Setting inactive server to port {0}...", ActiveLaunchParameters.PrimaryPort.Value); @@ -285,11 +288,11 @@ namespace Tgstation.Server.Host.Components.Watchdog if (!result) { logger.LogWarning("Failed to activate inactive server! Restarting monitor..."); - monitorState.NextAction = MonitorAction.Restart; //will dispose server + monitorState.NextAction = MonitorAction.Restart; // will dispose server return false; } - //inactive server should always be using active launch parameters + // inactive server should always be using active launch parameters LastLaunchParameters = ActiveLaunchParameters; var tmp = monitorState.ActiveServer; @@ -307,7 +310,7 @@ namespace Tgstation.Server.Host.Components.Watchdog async Task UpdateAndRestartInactiveServer(bool breakAfter) { activeParametersUpdated = new TaskCompletionSource(); - monitorState.InactiveServer.Dispose(); //kill or recycle it + monitorState.InactiveServer.Dispose(); // kill or recycle it var desiredNextAction = breakAfter ? MonitorAction.Break : MonitorAction.Continue; monitorState.NextAction = desiredNextAction; @@ -325,17 +328,17 @@ namespace Tgstation.Server.Host.Components.Watchdog catch (Exception e) { logger.LogError("Error occurred while recreating server! Attempting backup strategy of running DMB of running server! Exception: {0}", e.ToString()); - //ahh jeez, what do we do here? - //this is our fault, so it should never happen but - //idk maybe a database error while handling the newest dmb? - //either way try to start it using the active server's dmb as a backup + + // ahh jeez, what do we do here? + // this is our fault, so it should never happen but + // idk maybe a database error while handling the newest dmb? + // either way try to start it using the active server's dmb as a backup try { var dmbBackup = await dmbFactory.FromCompileJob(monitorState.ActiveServer.Dmb.CompileJob, cancellationToken).ConfigureAwait(false); - if (dmbBackup == null) //NANI!? - //just give up, if THAT compile job is failing then the ActiveServer is gonna crash soon too or already has - throw new JobException("Creating backup DMB provider failed!"); + if (dmbBackup == null) // NANI!? + throw new JobException("Creating backup DMB provider failed!"); // just give up, if THAT compile job is failing then the ActiveServer is gonna crash soon too or already has monitorState.InactiveServer = await sessionControllerFactory.LaunchNew(ActiveLaunchParameters, dmbBackup, null, false, !monitorState.ActiveServer.IsPrimary, false, cancellationToken).ConfigureAwait(false); monitorState.InactiveServer.SetHighPriority(); @@ -347,7 +350,7 @@ namespace Tgstation.Server.Host.Components.Watchdog } catch (Exception e2) { - //fuuuuucckkk + // fuuuuucckkk logger.LogError("Backup strategy failed! Monitor will restart when active server reboots! Exception: {0}", e2.ToString()); monitorState.InactiveServerCritFail = true; await chat.SendWatchdogMessage("Attempted reboot of inactive server failed. Watchdog will reset when active server fails or exits", cancellationToken).ConfigureAwait(false); @@ -361,13 +364,13 @@ namespace Tgstation.Server.Host.Components.Watchdog string ExitWord(ISessionController controller) => controller.TerminationWasRequested ? "exited" : "crashed"; - //reason handling + // reason handling switch (activationReason) { case MonitorActivationReason.ActiveServerCrashed: if (monitorState.ActiveServer.RebootState == Components.Watchdog.RebootState.Shutdown) { - //the time for graceful shutdown is now + // the time for graceful shutdown is now await chat.SendWatchdogMessage(String.Format(CultureInfo.InvariantCulture, "Active server {0}! Exiting due to graceful termination request...", ExitWord(monitorState.ActiveServer)), cancellationToken).ConfigureAwait(false); DisposeAndNullControllers(); monitorState.NextAction = MonitorAction.Exit; @@ -376,118 +379,122 @@ namespace Tgstation.Server.Host.Components.Watchdog if (FullRestartDeadInactive()) { - //tell chat about it and go ahead + // tell chat about it and go ahead await chat.SendWatchdogMessage(String.Format(CultureInfo.InvariantCulture, "Active server {0}! Inactive server unable to online!", ExitWord(monitorState.ActiveServer)), cancellationToken).ConfigureAwait(false); - //we've already been set to restart + + // we've already been set to restart break; } - //tell chat about it + // tell chat about it await chat.SendWatchdogMessage(String.Format(CultureInfo.InvariantCulture, "Active server {0}! Onlining inactive server...", ExitWord(monitorState.ActiveServer)), cancellationToken).ConfigureAwait(false); - //try to active the inactive server + // try to activate the inactive server if (!await MakeInactiveActive().ConfigureAwait(false)) - //failing that, we've already been set to restart - break; + break; // failing that, we've already been set to restart - //bring up another inactive server + // bring up another inactive server await UpdateAndRestartInactiveServer(true).ConfigureAwait(false); break; case MonitorActivationReason.InactiveServerCrashed: - //just announce and try to bring it back + // just announce and try to bring it back await chat.SendWatchdogMessage(String.Format(CultureInfo.InvariantCulture, "Inactive server {0}! Rebooting...", ExitWord(monitorState.InactiveServer)), cancellationToken).ConfigureAwait(false); await UpdateAndRestartInactiveServer(false).ConfigureAwait(false); break; case MonitorActivationReason.ActiveServerRebooted: - //ideal goal: active server just closed its port - //tell inactive server to open it's port and that's now the active server + // ideal goal: active server just closed its port + // tell inactive server to open it's port and that's now the active server var rebootState = monitorState.ActiveServer.RebootState; - monitorState.ActiveServer.ResetRebootState(); //the DMAPI has already done this internally + monitorState.ActiveServer.ResetRebootState(); // the DMAPI has already done this internally if (FullRestartDeadInactive() && rebootState != Components.Watchdog.RebootState.Shutdown) - //full restart if the inactive server is being fucky - break; + break; // full restart if the inactive server is being fucky - //what matters here is the RebootState + // what matters here is the RebootState var restartOnceSwapped = false; switch (rebootState) { case Components.Watchdog.RebootState.Normal: - //life as normal + // life as normal break; case Components.Watchdog.RebootState.Restart: - //reboot the current active server once the inactive one activates + // reboot the current active server once the inactive one activates restartOnceSwapped = true; break; case Components.Watchdog.RebootState.Shutdown: - //graceful shutdown time + // graceful shutdown time await chat.SendWatchdogMessage("Active server rebooted! Exiting due to graceful termination request...", cancellationToken).ConfigureAwait(false); DisposeAndNullControllers(); monitorState.NextAction = MonitorAction.Exit; return; + default: + Trace.Assert(false, String.Format(CultureInfo.InvariantCulture, "Invalid RebootState: {0}!", rebootState)); + break; } - //are both servers now running the same CompileJob? + // are both servers now running the same CompileJob? var sameCompileJob = monitorState.InactiveServer.Dmb.CompileJob.Id == monitorState.ActiveServer.Dmb.CompileJob.Id; - + if (!sameCompileJob || ActiveLaunchParameters != LastLaunchParameters) - //need a new launch to update either settings or compile job - restartOnceSwapped = true; + restartOnceSwapped = true; // need a new launch to update either settings or compile job if (restartOnceSwapped) - //we need to manually restart active server - //just kill it here, easier that way + /* + * we need to manually restart active server + * just kill it here, easier that way + */ monitorState.ActiveServer.Dispose(); var activeServerStillHasPortOpen = !restartOnceSwapped && !monitorState.ActiveServer.ClosePortOnReboot; if (activeServerStillHasPortOpen) - //we didn't want active server to swap for some reason and it still has it's port open - //just continue as normal + /* we didn't want active server to swap for some reason and it still has it's port open + * just continue as normal + */ break; - + if (!await MakeInactiveActive().ConfigureAwait(false)) - //monitor will restart - break; + break; // monitor will restart - //servers now swapped - - //enable this now if inactive server is not still valid + // servers now swapped + // enable this now if inactive server is not still valid monitorState.ActiveServer.ClosePortOnReboot = restartOnceSwapped; if (!restartOnceSwapped) - //now try to reopen it on the private port - //failing that, just reboot it + /* + * now try to reopen it on the private port + * failing that, just reboot it + */ restartOnceSwapped = !await monitorState.InactiveServer.SetPort(ActiveLaunchParameters.SecondaryPort.Value, cancellationToken).ConfigureAwait(false); - //break either way because any issues past this point would be solved by the reboot - if (restartOnceSwapped) - //for one reason or another - //update and reboot - await UpdateAndRestartInactiveServer(true).ConfigureAwait(false); + // break either way because any issues past this point would be solved by the reboot + if (restartOnceSwapped) // for one reason or another + await UpdateAndRestartInactiveServer(true).ConfigureAwait(false); // update and reboot else - //only skip checking inactive server rebooted, it's guaranteed InactiveServerStartup complete wouldn't fire this iteration - monitorState.NextAction = MonitorAction.Skip; + monitorState.NextAction = MonitorAction.Skip; // only skip checking inactive server rebooted, it's guaranteed InactiveServerStartup complete wouldn't fire this iteration break; case MonitorActivationReason.InactiveServerRebooted: - //just don't let the active server close it's port if the inactive server isn't ready + // just don't let the active server close it's port if the inactive server isn't ready monitorState.RebootingInactiveServer = true; monitorState.InactiveServer.ResetRebootState(); monitorState.ActiveServer.ClosePortOnReboot = false; monitorState.NextAction = MonitorAction.Continue; break; case MonitorActivationReason.InactiveServerStartupComplete: - //opposite of above case + // opposite of above case monitorState.RebootingInactiveServer = false; monitorState.ActiveServer.ClosePortOnReboot = true; monitorState.NextAction = MonitorAction.Continue; break; case MonitorActivationReason.NewDmbAvailable: case MonitorActivationReason.ActiveLaunchParametersUpdated: - //just reload the inactive server and wait for a swap to apply the changes + // just reload the inactive server and wait for a swap to apply the changes await UpdateAndRestartInactiveServer(true).ConfigureAwait(false); break; + default: + Trace.Assert(false, String.Format(CultureInfo.InvariantCulture, "Invalid monitor activation reason: {0}!", activationReason)); + break; } } @@ -500,15 +507,14 @@ namespace Tgstation.Server.Host.Components.Watchdog { logger.LogTrace("Entered MonitorLifetimes"); - //this function is responsible for calling HandlerMonitorWakeup when necessary and manitaining the MonitorState - + // this function is responsible for calling HandlerMonitorWakeup when necessary and manitaining the MonitorState var iteration = 1; for (var monitorState = new MonitorState(); monitorState.NextAction != MonitorAction.Exit; ++iteration) { - //always start out with continue + // always start out with continue monitorState.NextAction = MonitorAction.Continue; - //dump some info to the logs + // dump some info to the logs logger.LogDebug("Iteration {0} of monitor loop", iteration); try { @@ -516,12 +522,11 @@ namespace Tgstation.Server.Host.Components.Watchdog logger.LogDebug("Alpha is the active server"); else logger.LogDebug("Bravo is the active server"); - - + if (monitorState.RebootingInactiveServer) logger.LogDebug("Inactive server is rebooting"); - //update the monitor state with the inactive/active servers + // update the monitor state with the inactive/active servers monitorState.ActiveServer = AlphaIsActive ? alphaServer : bravoServer; monitorState.InactiveServer = AlphaIsActive ? bravoServer : alphaServer; @@ -533,7 +538,7 @@ namespace Tgstation.Server.Host.Components.Watchdog logger.LogDebug("Active server Compile Job ID: {0}", monitorState.ActiveServer.Dmb.CompileJob.Id); logger.LogDebug("Inactive server Compile Job ID: {0}", monitorState.InactiveServer.Dmb.CompileJob.Id); - //load the activation tasks into local variables + // load the activation tasks into local variables Task activeServerLifetime = monitorState.ActiveServer.Lifetime; Task inactiveServerLifetime = monitorState.InactiveServer.Lifetime; var activeServerReboot = monitorState.ActiveServer.OnReboot; @@ -542,14 +547,15 @@ namespace Tgstation.Server.Host.Components.Watchdog Task activeLaunchParametersChanged = activeParametersUpdated.Task; var newDmbAvailable = dmbFactory.OnNewerDmb; - //cancel waiting if requested + // cancel waiting if requested var cancelTcs = new TaskCompletionSource(); using (cancellationToken.Register(() => cancelTcs.SetCanceled())) { var toWaitOn = Task.WhenAny(activeServerLifetime, inactiveServerLifetime, activeServerReboot, inactiveServerReboot, newDmbAvailable, cancelTcs.Task, activeLaunchParametersChanged); if (monitorState.RebootingInactiveServer) toWaitOn = Task.WhenAny(toWaitOn, inactiveServerStartup); - //wait for something to happen + + // wait for something to happen await toWaitOn.ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); } @@ -557,14 +563,13 @@ namespace Tgstation.Server.Host.Components.Watchdog var chatTask = Task.CompletedTask; using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false)) { - //always run HandleMonitorWakeup from the context of the semaphore lock - //multiple things may have happened, handle them one at a time + // always run HandleMonitorWakeup from the context of the semaphore lock + // multiple things may have happened, handle them one at a time for (var moreActivationsToProcess = true; moreActivationsToProcess && (monitorState.NextAction == MonitorAction.Continue || monitorState.NextAction == MonitorAction.Skip);) { - MonitorActivationReason activationReason = default; //this will always be assigned before being used - - //process the tasks in this order and call HandlerMonitorWakup for each + MonitorActivationReason activationReason = default; // this will always be assigned before being used + // process the tasks in this order and call HandlerMonitorWakup for each bool CheckActivationReason(ref Task task, MonitorActivationReason testActivationReason) { var taskCompleted = task?.IsCompleted == true; @@ -576,8 +581,9 @@ namespace Tgstation.Server.Host.Components.Watchdog activationReason = testActivationReason; return true; } - return false; - }; + + return false; + } if (CheckActivationReason(ref activeServerLifetime, MonitorActivationReason.ActiveServerCrashed) || CheckActivationReason(ref inactiveServerLifetime, MonitorActivationReason.InactiveServerCrashed) @@ -591,12 +597,12 @@ namespace Tgstation.Server.Host.Components.Watchdog moreActivationsToProcess = false; } - //writeback alphaServer and bravoServer from monitor state in case they changesd + // writeback alphaServer and bravoServer from monitor state in case they changesd alphaServer = AlphaIsActive ? monitorState.ActiveServer : monitorState.InactiveServer; bravoServer = !AlphaIsActive ? monitorState.ActiveServer : monitorState.InactiveServer; } - //full reboot required + // full reboot required if (monitorState.NextAction == MonitorAction.Restart) { logger.LogDebug("Next state action is to restart"); @@ -609,12 +615,12 @@ namespace Tgstation.Server.Host.Components.Watchdog using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false)) try { - //use LaunchImplNoLock without announcements or restarting the monitor + // use LaunchImplNoLock without announcements or restarting the monitor await LaunchImplNoLock(false, false, null, cancellationToken).ConfigureAwait(false); if (Running) { logger.LogDebug("Relaunch successful, resetting monitor state..."); - monitorState = new MonitorState(); //clean the slate and continue + monitorState = new MonitorState(); // clean the slate and continue } } catch (OperationCanceledException) @@ -633,7 +639,7 @@ namespace Tgstation.Server.Host.Components.Watchdog logger.LogWarning("Failed to automatically restart the watchdog! Attempt: {0}", retryAttempts); else logger.LogWarning("Failed to automatically restart the watchdog! Attempt: {0}, Exception: {1}", retryAttempts, launchException); - var retryDelay = Math.Min(Math.Pow(2, retryAttempts), 3600); //max of one hour, increasing by a power of 2 each time + var retryDelay = Math.Min(Math.Pow(2, retryAttempts), 3600); // max of one hour, increasing by a power of 2 each time chatTask = chat.SendWatchdogMessage(String.Format(CultureInfo.InvariantCulture, "Failed to restart watchdog (Attempt: {0}), retrying in {1} seconds...", retryAttempts, retryDelay), cancellationToken); await Task.WhenAll(asyncDelayer.Delay(TimeSpan.FromSeconds(retryDelay), cancellationToken), chatTask).ConfigureAwait(false); } @@ -647,11 +653,12 @@ namespace Tgstation.Server.Host.Components.Watchdog } catch (Exception e) { - //really, this should NEVER happen + // really, this should NEVER happen logger.LogError("Monitor crashed! Iteration: {0}, State: {1}, Exception: {2}", iteration, JsonConvert.SerializeObject(monitorState), e); await chat.SendWatchdogMessage(String.Format(CultureInfo.InvariantCulture, "Monitor crashed, this should NEVER happen! Please report this, full details in logs! Restarting monitor... Error: {0}", e.Message), cancellationToken).ConfigureAwait(false); } } + logger.LogTrace("Monitor exiting..."); } @@ -671,7 +678,6 @@ namespace Tgstation.Server.Host.Components.Watchdog return true; } - /// public async Task ChangeSettings(DreamDaemonLaunchParameters launchParameters, CancellationToken cancellationToken) { @@ -681,8 +687,7 @@ namespace Tgstation.Server.Host.Components.Watchdog return; ActiveLaunchParameters = launchParameters; if (Running) - //queue an update - activeParametersUpdated.TrySetResult(null); + activeParametersUpdated.TrySetResult(null); // queue an update } } @@ -701,67 +706,68 @@ namespace Tgstation.Server.Host.Components.Watchdog if (Running) throw new JobException("Watchdog already running!"); + // this is necessary, the monitor could be in it's sleep loop trying to restart, if so cancel THAT monitor and start our own with blackjack and hookers Task chatTask; - //this is necessary, the monitor could be in it's sleep loop trying to restart, if so cancel THAT monitor and start our own with blackjack and hookers if (startMonitor && await StopMonitor().ConfigureAwait(false)) chatTask = chat.SendWatchdogMessage("Automatic retry sequence cancelled by manual launch. Restarting...", cancellationToken); else if (announce) - //simple announce - chatTask = chat.SendWatchdogMessage(reattachInfo == null ? "Starting..." : "Reattaching...", cancellationToken); + chatTask = chat.SendWatchdogMessage(reattachInfo == null ? "Starting..." : "Reattaching...", cancellationToken); // simple announce else - //no announce - chatTask = Task.CompletedTask; - - //since neither server is running, this is safe to do + chatTask = Task.CompletedTask; // no announce + + // since neither server is running, this is safe to do LastLaunchParameters = ActiveLaunchParameters; - //for when we call ourself and want to not catch thrown exceptions + // for when we call ourself and want to not catch thrown exceptions var ignoreNestedException = false; try { - //good ole sanity, should never fucking trigger but i don't trust myself even though I should + // good ole sanity, should never fucking trigger but i don't trust myself even though I should + // TODO: Unit test this instead? if (alphaServer != null || bravoServer != null) throw new InvalidOperationException("Entered LaunchNoLock with one or more of the servers not being null!"); - - //don't need a new dmb if reattaching + + // don't need a new dmb if reattaching var doesntNeedNewDmb = reattachInfo?.Alpha != null && reattachInfo?.Bravo != null; var dmbToUse = doesntNeedNewDmb ? null : dmbFactory.LockNextDmb(2); - //if this try catches something, both servers are killed + // if this try catches something, both servers are killed try { - //start the alpha server task, either by launch a new process or attaching to an existing one - //The tasks returned are mainly for writing interop files to the directories among other things and should generally never fail - //The tasks pertaining to server startup times are in the ISessionControllers + // start the alpha server task, either by launch a new process or attaching to an existing one + // The tasks returned are mainly for writing interop files to the directories among other things and should generally never fail + // The tasks pertaining to server startup times are in the ISessionControllers Task alphaServerTask; if (!doesntNeedNewDmb) alphaServerTask = sessionControllerFactory.LaunchNew(ActiveLaunchParameters, dmbToUse, null, true, true, false, cancellationToken); else alphaServerTask = sessionControllerFactory.Reattach(reattachInfo.Alpha, cancellationToken); - //retrieve the session controller + // retrieve the session controller var startTime = DateTimeOffset.Now; alphaServer = await alphaServerTask.ConfigureAwait(false); - //failed reattaches will return null + + // failed reattaches will return null alphaServer?.SetHighPriority(); - //extra delay for total ordering + // extra delay for total ordering var now = DateTimeOffset.Now; var delay = now - startTime; - //definitely not if reattaching though + // definitely not if reattaching though if (reattachInfo == null && delay.TotalSeconds < AlphaBravoStartupSeperationInterval) await asyncDelayer.Delay(startTime.AddSeconds(AlphaBravoStartupSeperationInterval) - now, cancellationToken).ConfigureAwait(false); - //now bring bravo up + // now bring bravo up if (!doesntNeedNewDmb) bravoServer = await sessionControllerFactory.LaunchNew(ActiveLaunchParameters, dmbToUse, null, false, false, false, cancellationToken).ConfigureAwait(false); else bravoServer = await sessionControllerFactory.Reattach(reattachInfo.Bravo, cancellationToken).ConfigureAwait(false); - //failed reattaches will return null + + // failed reattaches will return null bravoServer?.SetHighPriority(); - //possiblity of null servers due to failed reattaches + // possiblity of null servers due to failed reattaches if (alphaServer == null || bravoServer == null) { await chatTask.ConfigureAwait(false); @@ -770,7 +776,7 @@ namespace Tgstation.Server.Host.Components.Watchdog || (alphaServer == null && reattachInfo.AlphaIsActive) || (bravoServer == null && !reattachInfo.AlphaIsActive)) { - //we lost the active server, just restart entirely + // we lost the active server, just restart entirely DisposeAndNullControllers(); const string FailReattachMessage = "Unable to properly reattach to active server! Restarting..."; logger.LogWarning(FailReattachMessage); @@ -781,7 +787,7 @@ namespace Tgstation.Server.Host.Components.Watchdog return; } - //we still have the active server but the other one is dead to us, hand it off to the monitor to restart + // we still have the active server but the other one is dead to us, hand it off to the monitor to restart const string InactiveReattachFailureMessage = "Unable to reattach to inactive server. Leaving for monitor to reboot..."; chatTask = chat.SendWatchdogMessage(InactiveReattachFailureMessage, cancellationToken); logger.LogWarning(InactiveReattachFailureMessage); @@ -792,13 +798,12 @@ namespace Tgstation.Server.Host.Components.Watchdog alphaServer = sessionControllerFactory.CreateDeadSession(reattachInfo.Alpha.Dmb); } - //throws a JobException if something went wrong with a launch - //Dead sessions won't trigger this + // throws a JobException if something went wrong with a launch + // Dead sessions won't trigger this async Task CheckLaunch(ISessionController controller, string serverName) { var launch = await controller.LaunchResult.ConfigureAwait(false); - if (launch.ExitCode.HasValue) - //you killed us ray... + if (launch.ExitCode.HasValue) // you killed us ray... throw new JobException(String.Format(CultureInfo.InvariantCulture, "{1} server failed to start: {0}", launch.ToString(), serverName)); if (!launch.StartupTime.HasValue) throw new JobException(String.Format(CultureInfo.InvariantCulture, "{1} server timed out on startup: {0}s", launch.ToString(), ActiveLaunchParameters.StartupTimeout.Value)); @@ -807,10 +812,11 @@ namespace Tgstation.Server.Host.Components.Watchdog var alphaLrt = CheckLaunch(alphaServer, "Alpha"); var bravoLrt = CheckLaunch(bravoServer, "Bravo"); - //this task completes when both serers have finished booting + + // this task completes when both serers have finished booting var allTask = Task.WhenAll(alphaLrt, bravoLrt); - //don't forget about the cancellationToken + // don't forget about the cancellationToken var cancelTcs = new TaskCompletionSource(); using (cancellationToken.Register(() => cancelTcs.SetCanceled())) await Task.WhenAny(allTask, cancelTcs.Task).ConfigureAwait(false); @@ -818,7 +824,7 @@ namespace Tgstation.Server.Host.Components.Watchdog await allTask.ConfigureAwait(false); - //both servers are now running, alpha is the active server(unless reattach), huzzah + // both servers are now running, alpha is the active server(unless reattach), huzzah AlphaIsActive = reattachInfo?.AlphaIsActive ?? true; var activeServer = AlphaIsActive ? alphaServer : bravoServer; @@ -838,34 +844,32 @@ namespace Tgstation.Server.Host.Components.Watchdog { if (dmbToUse != null) { - //we locked 2 dmbs + // we locked 2 dmbs if (bravoServer == null) { - //bravo didn't get control of his + // bravo didn't get control of his dmbToUse.Dispose(); if (alphaServer == null) - //alpha didn't get control of his - dmbToUse.Dispose(); + dmbToUse.Dispose(); // alpha didn't get control of his } } - else if (doesntNeedNewDmb) - //we have reattachInfo + else if (doesntNeedNewDmb) // we have reattachInfo if (bravoServer == null) { - //bravo didn't get control of his + // bravo didn't get control of his reattachInfo.Bravo?.Dmb.Dispose(); if (alphaServer == null) - //alpha didn't get control of his - reattachInfo.Alpha?.Dmb.Dispose(); + reattachInfo.Alpha?.Dmb.Dispose(); // alpha didn't get control of his } - //kill the controllers + + // kill the controllers DisposeAndNullControllers(); throw; } } catch (Exception e) { - //don't try to send chat tasks or warning logs if were suppressing exceptions or cancelled + // don't try to send chat tasks or warning logs if were suppressing exceptions or cancelled if (!ignoreNestedException && !cancellationToken.IsCancellationRequested) { var originalChatTask = chatTask; @@ -874,14 +878,16 @@ namespace Tgstation.Server.Host.Components.Watchdog await originalChatTask.ConfigureAwait(false); await chat.SendWatchdogMessage("Startup failed!", cancellationToken).ConfigureAwait(false); } + chatTask = ChainChatTaskWithErrorMessage(); logger.LogWarning("Failed to start watchdog: {0}", e.ToString()); } + throw; } finally { - //finish the chat task that's in flight + // finish the chat task that's in flight try { await chatTask.ConfigureAwait(false); @@ -929,6 +935,7 @@ namespace Tgstation.Server.Host.Components.Watchdog await LaunchImplNoLock(true, !Running, null, cancellationToken).ConfigureAwait(false); await chatTask.ConfigureAwait(false); } + var toReboot = AlphaIsActive ? alphaServer : bravoServer; if (toReboot != null) { @@ -994,6 +1001,7 @@ namespace Tgstation.Server.Host.Components.Watchdog reattachInformation.Bravo = bravoServer?.Release(); await reattachInfoHandler.Save(reattachInformation, cancellationToken).ConfigureAwait(false); } + await Terminate(false, cancellationToken).ConfigureAwait(false); } diff --git a/src/Tgstation.Server.Host/Components/Watchdog/WindowsNetworkPromptReaper.cs b/src/Tgstation.Server.Host/Components/Watchdog/WindowsNetworkPromptReaper.cs index 719eb9f4f8..737d053e09 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/WindowsNetworkPromptReaper.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/WindowsNetworkPromptReaper.cs @@ -19,7 +19,7 @@ namespace Tgstation.Server.Host.Components.Watchdog /// Number of times to send the button click message. Should be at least 2 or it may fail to focus the window /// const int SendMessageCount = 5; - + /// /// Check for prompts each time this amount of milliseconds pass /// @@ -41,7 +41,7 @@ namespace Tgstation.Server.Host.Components.Watchdog readonly CancellationTokenSource cancellationTokenSource; /// - /// The list of s registered + /// The list of s registered /// readonly List registeredProcesses; @@ -77,6 +77,7 @@ namespace Tgstation.Server.Host.Components.Watchdog { gcChildhandlesList.Free(); } + return childHandles; } @@ -117,12 +118,12 @@ namespace Tgstation.Server.Host.Components.Watchdog if (window == IntPtr.Zero) continue; - //found a bitch + // found a bitch var threadId = NativeMethods.GetWindowThreadProcessId(window, out processId); if (!registeredProcesses.Any(x => x.Id == processId)) - //not our bitch - continue; + continue; // not our bitch } + logger.LogTrace("Identified \"Network Accessibility\" window in owned process {0}", processId); var found = false; @@ -140,13 +141,14 @@ namespace Tgstation.Server.Host.Components.Watchdog var windowText = stringBuilder.ToString(); if (windowText == "Yes") { - //smash_button_meme.jpg + // smash_button_meme.jpg logger.LogTrace("Sending \"Yes\" button clicks..."); for (var J = 0; J < SendMessageCount; ++J) { const int BM_CLICK = 0x00F5; var result = NativeMethods.SendMessage(I, BM_CLICK, IntPtr.Zero, IntPtr.Zero); } + found = true; break; } diff --git a/src/Tgstation.Server.Host/Configuration/DatabaseType.cs b/src/Tgstation.Server.Host/Configuration/DatabaseType.cs index 2ca7d8fafc..c4b5f31d67 100644 --- a/src/Tgstation.Server.Host/Configuration/DatabaseType.cs +++ b/src/Tgstation.Server.Host/Configuration/DatabaseType.cs @@ -9,10 +9,12 @@ /// Use Microsoft SQL Server /// SqlServer, + /// /// Use MySQL /// MySql, + /// /// Use MariaDB /// diff --git a/src/Tgstation.Server.Host/Configuration/FileLoggingConfiguration.cs b/src/Tgstation.Server.Host/Configuration/FileLoggingConfiguration.cs index e10a6634ed..0251efb803 100644 --- a/src/Tgstation.Server.Host/Configuration/FileLoggingConfiguration.cs +++ b/src/Tgstation.Server.Host/Configuration/FileLoggingConfiguration.cs @@ -28,7 +28,7 @@ namespace Tgstation.Server.Host.Configuration /// Where log files are stored /// public string Directory { get; set; } - + /// /// If file logging is disabled /// @@ -40,7 +40,6 @@ namespace Tgstation.Server.Host.Configuration [JsonConverter(typeof(StringEnumConverter))] public LogLevel LogLevel { get; set; } = DefaultLogLevel; - /// /// The minimum to display in logs for Microsoft library sources /// diff --git a/src/Tgstation.Server.Host/Configuration/SetupWizardMode.cs b/src/Tgstation.Server.Host/Configuration/SetupWizardMode.cs index 4f2b59204a..b5039d15c8 100644 --- a/src/Tgstation.Server.Host/Configuration/SetupWizardMode.cs +++ b/src/Tgstation.Server.Host/Configuration/SetupWizardMode.cs @@ -9,14 +9,17 @@ /// Run the wizard if the appsettings.{Environment}.json is not present or empty /// Autodetect, + /// /// Force run the wizard /// Force, + /// /// Only run the wizard and exit /// Only, + /// /// Never run the wizard /// diff --git a/src/Tgstation.Server.Host/Controllers/AdministrationController.cs b/src/Tgstation.Server.Host/Controllers/AdministrationController.cs index 0899ab7db5..605be61107 100644 --- a/src/Tgstation.Server.Host/Controllers/AdministrationController.cs +++ b/src/Tgstation.Server.Host/Controllers/AdministrationController.cs @@ -8,7 +8,6 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Net; -using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api; @@ -126,6 +125,7 @@ namespace Tgstation.Server.Host.Controllers { Logger.LogWarning("Not found exception while retrieving upstream repository info: {0}", e); } + return Json(new Administration { LatestVersion = greatestVersion, @@ -188,19 +188,22 @@ namespace Tgstation.Server.Host.Controllers var asset = release.Assets.Where(x => x.Name == updatesConfiguration.UpdatePackageAssetName).FirstOrDefault(); if (asset == default) continue; - + if (!serverUpdater.ApplyUpdate(version, new Uri(asset.BrowserDownloadUrl), ioManager)) return Conflict(new ErrorMessage { Message = "An update operation is already in progress!" }); - return Accepted(); //gtfo of here before all the cancellation tokens fire + return Accepted(); // gtfo of here before all the cancellation tokens fire } return StatusCode((int)HttpStatusCode.Gone); } - /// + /// + /// Attempts to restart the server + /// + /// A resulting in the of the request [HttpDelete] [TgsAuthorize(AdministrationRights.RestartHost)] public async Task Delete() @@ -215,6 +218,7 @@ namespace Tgstation.Server.Host.Controllers Message = RestartNotSupportedException }); } + await serverUpdater.Restart().ConfigureAwait(false); return Ok(); } diff --git a/src/Tgstation.Server.Host/Controllers/ByondController.cs b/src/Tgstation.Server.Host/Controllers/ByondController.cs index 49e4c38be7..10229aaef1 100644 --- a/src/Tgstation.Server.Host/Controllers/ByondController.cs +++ b/src/Tgstation.Server.Host/Controllers/ByondController.cs @@ -47,7 +47,7 @@ namespace Tgstation.Server.Host.Controllers /// [TgsAuthorize(ByondRights.ReadActive)] - public override Task Read(CancellationToken cancellationToken) => Task.FromResult((IActionResult) + public override Task Read(CancellationToken cancellationToken) => Task.FromResult( Json(new Api.Models.Byond { Version = instanceManager.GetInstance(Instance).ByondManager.ActiveVersion @@ -55,7 +55,7 @@ namespace Tgstation.Server.Host.Controllers /// [TgsAuthorize(ByondRights.ListInstalled)] - public override Task List(CancellationToken cancellationToken) => Task.FromResult((IActionResult) + public override Task List(CancellationToken cancellationToken) => Task.FromResult( Json(instanceManager.GetInstance(Instance).ByondManager.InstalledVersions.Select(x => new Api.Models.Byond { Version = x @@ -73,7 +73,7 @@ namespace Tgstation.Server.Host.Controllers var byondManager = instanceManager.GetInstance(Instance).ByondManager; - //remove cruff fields + // remove cruff fields var installingVersion = new Version(model.Version.Major, model.Version.Minor); var result = new Api.Models.Byond(); @@ -86,7 +86,8 @@ namespace Tgstation.Server.Host.Controllers else { Logger.LogInformation("User ID {0} installing BYOND version to {2} on instance ID {1}", AuthenticationContext.User.Id, Instance.Id, installingVersion); - //run the install through the job manager + + // run the install through the job manager var job = new Models.Job { Description = String.Format(CultureInfo.InvariantCulture, "Install BYOND version {0}", installingVersion), @@ -98,6 +99,7 @@ namespace Tgstation.Server.Host.Controllers await jobManager.RegisterOperation(job, (paramJob, databaseContext, progressHandler, ct) => byondManager.ChangeVersion(installingVersion, ct), cancellationToken).ConfigureAwait(false); result.InstallJob = job.ToApi(); } + if ((AuthenticationContext.GetRight(RightsType.Byond) & (ulong)ByondRights.ReadActive) != 0) result.Version = byondManager.ActiveVersion; return result.InstallJob != null ? (IActionResult)Accepted(result) : Json(result); diff --git a/src/Tgstation.Server.Host/Controllers/ChatController.cs b/src/Tgstation.Server.Host/Controllers/ChatController.cs index 2e6d3a9ebc..391f9a27c8 100644 --- a/src/Tgstation.Server.Host/Controllers/ChatController.cs +++ b/src/Tgstation.Server.Host/Controllers/ChatController.cs @@ -87,13 +87,13 @@ namespace Tgstation.Server.Host.Controllers model.Enabled = model.Enabled ?? false; - //try to update das db first + // try to update das db first var dbModel = new Models.ChatBot { Name = model.Name, ConnectionString = model.ConnectionString, Enabled = model.Enabled, - Channels = model.Channels?.Select(x => ConvertApiChatChannel(x)).ToList() ?? new List(), //important that this isn't null + Channels = model.Channels?.Select(x => ConvertApiChatChannel(x)).ToList() ?? new List(), // important that this isn't null InstanceId = Instance.Id, Provider = model.Provider, }; @@ -106,7 +106,7 @@ namespace Tgstation.Server.Host.Controllers { try { - //try to create it + // try to create it var instance = instanceManager.GetInstance(Instance); await instance.Chat.ChangeSettings(dbModel, cancellationToken).ConfigureAwait(false); @@ -115,7 +115,7 @@ namespace Tgstation.Server.Host.Controllers } catch { - //undo the add + // undo the add DatabaseContext.ChatBots.Remove(dbModel); await DatabaseContext.Save(default).ConfigureAwait(false); throw; @@ -125,6 +125,7 @@ namespace Tgstation.Server.Host.Controllers { return BadRequest(new ErrorMessage { Message = e.Message }); } + return StatusCode((int)HttpStatusCode.Created, dbModel.ToApi()); } @@ -208,7 +209,7 @@ namespace Tgstation.Server.Host.Controllers property.SetValue(current, newVal); anySettingsModified = true; return false; - }; + } var oldProvider = current.Provider; @@ -238,8 +239,7 @@ namespace Tgstation.Server.Host.Controllers var chat = instanceManager.GetInstance(Instance).Chat; if (anySettingsModified) - //have to rebuild the thing first - await chat.ChangeSettings(current, cancellationToken).ConfigureAwait(false); + await chat.ChangeSettings(current, cancellationToken).ConfigureAwait(false); // have to rebuild the thing first if (model.Channels != null || anySettingsModified) await chat.ChangeChannels(current.Id, current.Channels, cancellationToken).ConfigureAwait(false); @@ -250,6 +250,7 @@ namespace Tgstation.Server.Host.Controllers current.ConnectionString = null; return Json(current.ToApi()); } + return Ok(); } } diff --git a/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs b/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs index a893ea3735..49bbb78e67 100644 --- a/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs +++ b/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs @@ -58,6 +58,7 @@ namespace Tgstation.Server.Host.Controllers systemIdentityToUse = null; return true; } + systemIdentityToUse = Instance.ConfigurationType == ConfigurationType.SystemIdentityWrite ? AuthenticationContext.SystemIdentity : null; return false; } diff --git a/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs b/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs index ed43af0cf2..232e6d1d4e 100644 --- a/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs +++ b/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs @@ -29,6 +29,7 @@ namespace Tgstation.Server.Host.Controllers /// The for the /// readonly IJobManager jobManager; + /// /// The for the /// @@ -52,7 +53,7 @@ namespace Tgstation.Server.Host.Controllers [TgsAuthorize(DreamDaemonRights.Start)] public override async Task Create([FromBody] DreamDaemon model, CancellationToken cancellationToken) { - //alias for launching DD + // alias for launching DD var instance = instanceManager.GetInstance(Instance); if (instance.Watchdog.Running) @@ -94,7 +95,7 @@ namespace Tgstation.Server.Host.Controllers if (settings == default) return StatusCode((int)HttpStatusCode.Gone); } - + var result = new DreamDaemon(); if (metadata) { @@ -113,7 +114,7 @@ namespace Tgstation.Server.Host.Controllers result.SoftRestart = rstate == RebootState.Restart; result.SoftShutdown = rstate == RebootState.Shutdown; result.StartupTimeout = settings.StartupTimeout; - }; + } if (revision) { @@ -126,12 +127,15 @@ namespace Tgstation.Server.Host.Controllers return Json(result); } - /// + /// + /// Stops DreamDaemon if it's running + /// + /// The for the operation + /// A resulting in the of the operation [HttpDelete] [TgsAuthorize(DreamDaemonRights.Shutdown)] public async Task Delete(CancellationToken cancellationToken) { - //alias for stopping DD var instance = instanceManager.GetInstance(Instance); await instance.Watchdog.Terminate(false, cancellationToken).ConfigureAwait(false); return Ok(); @@ -150,7 +154,7 @@ namespace Tgstation.Server.Host.Controllers if (model.SecurityLevel == DreamDaemonSecurity.Ultrasafe) return BadRequest(new ErrorMessage { Message = "This version of TGS does not support the ultrasafe DreamDaemon configuration!" }); - //alias for changing DD settings + // alias for changing DD settings var current = await DatabaseContext.Instances.Where(x => x.Id == Instance.Id).Select(x => x.DreamDaemonSettings).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); if (current == default) @@ -171,7 +175,7 @@ namespace Tgstation.Server.Host.Controllers property.SetValue(current, newVal); return false; - }; + } var oldSoftRestart = current.SoftRestart; var oldSoftShutdown = current.SoftShutdown; @@ -190,9 +194,10 @@ namespace Tgstation.Server.Host.Controllers return BadRequest(new ErrorMessage { Message = "Primary port and secondary port cannot be the same!" }); var wd = instanceManager.GetInstance(Instance).Watchdog; - + await DatabaseContext.Save(cancellationToken).ConfigureAwait(false); - //run this second because current may be modified by it + + // run this second because current may be modified by it await wd.ChangeSettings(current, cancellationToken).ConfigureAwait(false); if (!oldSoftRestart.Value && current.SoftRestart.Value) diff --git a/src/Tgstation.Server.Host/Controllers/DreamMakerController.cs b/src/Tgstation.Server.Host/Controllers/DreamMakerController.cs index 2e4a718ac1..879caa821b 100644 --- a/src/Tgstation.Server.Host/Controllers/DreamMakerController.cs +++ b/src/Tgstation.Server.Host/Controllers/DreamMakerController.cs @@ -20,12 +20,13 @@ namespace Tgstation.Server.Host.Controllers /// Controller for managing the compiler /// [Route(Routes.DreamMaker)] - public sealed class DreamMakerController : ModelController + public sealed class DreamMakerController : ModelController { /// /// The for the /// readonly IJobManager jobManager; + /// /// The for the /// diff --git a/src/Tgstation.Server.Host/Controllers/HomeController.cs b/src/Tgstation.Server.Host/Controllers/HomeController.cs index 3e590e7e97..bc06f146ed 100644 --- a/src/Tgstation.Server.Host/Controllers/HomeController.cs +++ b/src/Tgstation.Server.Host/Controllers/HomeController.cs @@ -3,7 +3,6 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using System; using System.Linq; -using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api; @@ -23,18 +22,22 @@ namespace Tgstation.Server.Host.Controllers /// The for the /// readonly ITokenFactory tokenFactory; + /// /// The for the /// readonly ISystemIdentityFactory systemIdentityFactory; + /// /// The for the /// readonly ICryptographySuite cryptographySuite; + /// /// The for the /// readonly IApplication application; + /// /// The for the /// @@ -86,13 +89,14 @@ namespace Tgstation.Server.Host.Controllers ISystemIdentity identity; try { - //trust the system over the database because a user's name can change while still having the same SID + // trust the system over the database because a user's name can change while still having the same SID identity = await systemIdentityFactory.CreateSystemIdentity(ApiHeaders.Username, ApiHeaders.Password, cancellationToken).ConfigureAwait(false); } catch (NotImplementedException) { identity = null; } + using (identity) { IQueryable query; @@ -127,7 +131,8 @@ namespace Tgstation.Server.Host.Controllers await DatabaseContext.Save(cancellationToken).ConfigureAwait(false); } } - //check if the name changed and updoot accordingly + + // check if the name changed and updoot accordingly else if (identity.Username != user.Name) { DatabaseContext.Users.Attach(user); @@ -142,7 +147,7 @@ namespace Tgstation.Server.Host.Controllers var token = await tokenFactory.CreateToken(user, cancellationToken).ConfigureAwait(false); if (identity != null) { - //expire the identity slightly after the auth token in case of lag + // expire the identity slightly after the auth token in case of lag var identExpiry = token.ExpiresAt.Value; identExpiry += tokenFactory.ValidationParameters.ClockSkew; identExpiry += TimeSpan.FromSeconds(15); diff --git a/src/Tgstation.Server.Host/Controllers/InstanceController.cs b/src/Tgstation.Server.Host/Controllers/InstanceController.cs index e220a74e39..bf4f9138f4 100644 --- a/src/Tgstation.Server.Host/Controllers/InstanceController.cs +++ b/src/Tgstation.Server.Host/Controllers/InstanceController.cs @@ -87,6 +87,7 @@ namespace Tgstation.Server.Host.Controllers absolutePath = null; return; } + absolutePath = ioManager.ResolvePath(model.Path); if (platformIdentifier.IsWindows) model.Path = absolutePath.ToUpperInvariant(); @@ -130,7 +131,6 @@ namespace Tgstation.Server.Host.Controllers if (rawPath.StartsWith(normalizedLocalPath, StringComparison.Ordinal)) return Conflict("Instances cannot be created in the installation directory!"); - var dirExistsTask = ioManager.DirectoryExists(model.Path, cancellationToken); bool attached = false; if (await ioManager.FileExists(model.Path, cancellationToken).ConfigureAwait(false) || await dirExistsTask.ConfigureAwait(false)) @@ -171,8 +171,7 @@ namespace Tgstation.Server.Host.Controllers AutoUpdatesKeepTestMerges = false, AutoUpdatesSynchronize = false }, - //give this user full privileges on the instance - InstanceUsers = new List + InstanceUsers = new List // give this user full privileges on the instance { InstanceAdminUser() } @@ -185,13 +184,13 @@ namespace Tgstation.Server.Host.Controllers try { - //actually reserve it now + // actually reserve it now await ioManager.CreateDirectory(rawPath, cancellationToken).ConfigureAwait(false); await ioManager.DeleteFile(ioManager.ConcatPath(rawPath, InstanceAttachFileName), cancellationToken).ConfigureAwait(false); } catch { - //oh shit delete the model + // oh shit delete the model DatabaseContext.Instances.Remove(newInstance); await DatabaseContext.Save(default).ConfigureAwait(false); @@ -243,7 +242,7 @@ namespace Tgstation.Server.Host.Controllers var attachFileName = ioManager.ConcatPath(originalModel.Path, InstanceAttachFileName); await ioManager.WriteAllBytes(attachFileName, Array.Empty(), default).ConfigureAwait(false); - await DatabaseContext.Save(cancellationToken).ConfigureAwait(false); //cascades everything + await DatabaseContext.Save(cancellationToken).ConfigureAwait(false); // cascades everything return Ok(); } @@ -264,8 +263,7 @@ namespace Tgstation.Server.Host.Controllers }).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); if (moveJob != default) - //cancel it now - await jobManager.CancelJob(moveJob, AuthenticationContext.User, true, cancellationToken).ConfigureAwait(false); + await jobManager.CancelJob(moveJob, AuthenticationContext.User, true, cancellationToken).ConfigureAwait(false); // cancel it now var usersInstanceUserTask = instanceQuery.SelectMany(x => x.InstanceUsers).Where(x => x.UserId == AuthenticationContext.User.Id).FirstOrDefaultAsync(cancellationToken); @@ -273,7 +271,7 @@ namespace Tgstation.Server.Host.Controllers .Include(x => x.RepositorySettings) .Include(x => x.ChatSettings) .ThenInclude(x => x.Channels) - .Include(x => x.DreamDaemonSettings) //need these for onlining + .Include(x => x.DreamDaemonSettings) // need these for onlining .FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); if (originalModel == default(Models.Instance)) return StatusCode((int)HttpStatusCode.Gone); @@ -292,7 +290,7 @@ namespace Tgstation.Server.Host.Controllers property.SetValue(originalModel, newVal); return false; - }; + } string originalModelPath = null; string rawPath = null; @@ -326,7 +324,7 @@ namespace Tgstation.Server.Host.Controllers || CheckModified(x => x.Online, InstanceManagerRights.SetOnline)) return Forbid(); - //ensure the current user has write privilege on the instance + // ensure the current user has write privilege on the instance var usersInstanceUser = await usersInstanceUserTask.ConfigureAwait(false); if (usersInstanceUser == default) { @@ -349,8 +347,8 @@ namespace Tgstation.Server.Host.Controllers await instanceManager.OfflineInstance(originalModel, AuthenticationContext.User, cancellationToken).ConfigureAwait(false); else if (!originalOnline && model.Online == true) { - //force autostart false here because we don't want any long running jobs right now - //remember to document this + // force autostart false here because we don't want any long running jobs right now + // remember to document this originalModel.DreamDaemonSettings.AutoStart = false; await instanceManager.OnlineInstance(originalModel, cancellationToken).ConfigureAwait(false); } @@ -412,7 +410,7 @@ namespace Tgstation.Server.Host.Controllers var apis = instances.Select(x => x.ToApi()); var moveJobs = await moveJobTasks.ConfigureAwait(false); foreach(var I in moveJobs) - apis.Where(x => x.Id == I.Instance.Id).First().MoveJob = I.ToApi(); //if this .First() fails i will personally murder kevinz000 because I just know he is somehow responsible + apis.Where(x => x.Id == I.Instance.Id).First().MoveJob = I.ToApi(); // if this .First() fails i will personally murder kevinz000 because I just know he is somehow responsible return Json(apis); } @@ -440,7 +438,7 @@ namespace Tgstation.Server.Host.Controllers if (cantList && !instance.InstanceUsers.Any(x => x.UserId == AuthenticationContext.User.Id && x.AnyRights)) return Forbid(); - + var api = instance.ToApi(); api.MoveJob = (await moveJobTask.ConfigureAwait(false))?.ToApi(); return Json(api); diff --git a/src/Tgstation.Server.Host/Controllers/InstanceUserController.cs b/src/Tgstation.Server.Host/Controllers/InstanceUserController.cs index e28011d67c..26e9a1ab00 100644 --- a/src/Tgstation.Server.Host/Controllers/InstanceUserController.cs +++ b/src/Tgstation.Server.Host/Controllers/InstanceUserController.cs @@ -27,7 +27,7 @@ namespace Tgstation.Server.Host.Controllers /// The for the /// The for the /// The for the - public InstanceUserController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, ILogger logger) : base(databaseContext, authenticationContextFactory, logger, true) //false instance requirement, we handle this ourself + public InstanceUserController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, ILogger logger) : base(databaseContext, authenticationContextFactory, logger, true) // false instance requirement, we handle this ourself { } /// @@ -116,7 +116,7 @@ namespace Tgstation.Server.Host.Controllers [TgsAuthorize(InstanceUserRights.ReadUsers)] public override async Task GetId(long id, CancellationToken cancellationToken) { - //this functions as userId + // this functions as userId var user = await DatabaseContext.Instances.Where(x => x.Id == Instance.Id).SelectMany(x => x.InstanceUsers).Where(x => x.UserId == id).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); if (user == default) return StatusCode((int)HttpStatusCode.Gone); diff --git a/src/Tgstation.Server.Host/Controllers/JobController.cs b/src/Tgstation.Server.Host/Controllers/JobController.cs index d959a9a0eb..e5c08bbc99 100644 --- a/src/Tgstation.Server.Host/Controllers/JobController.cs +++ b/src/Tgstation.Server.Host/Controllers/JobController.cs @@ -48,7 +48,7 @@ namespace Tgstation.Server.Host.Controllers [TgsAuthorize] public override async Task List(CancellationToken cancellationToken) { - //you KNOW this will need pagination eventually right? + // you KNOW this will need pagination eventually right? var jobs = await DatabaseContext.Jobs.Where(x => x.Instance.Id == Instance.Id).OrderByDescending(x => x.StartedAt).Select(x => new Api.Models.Job { Id = x.Id @@ -60,7 +60,7 @@ namespace Tgstation.Server.Host.Controllers [TgsAuthorize] public override async Task Delete(long id, CancellationToken cancellationToken) { - //don't care if an instance post or not at this point + // don't care if an instance post or not at this point var job = await DatabaseContext.Jobs.Where(x => x.Id == id).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); if (job == default(Job)) return NotFound(); diff --git a/src/Tgstation.Server.Host/Controllers/RepositoryController.cs b/src/Tgstation.Server.Host/Controllers/RepositoryController.cs index 5c82f36345..93060229b4 100644 --- a/src/Tgstation.Server.Host/Controllers/RepositoryController.cs +++ b/src/Tgstation.Server.Host/Controllers/RepositoryController.cs @@ -83,24 +83,26 @@ namespace Tgstation.Server.Host.Controllers var needsDbUpdate = revisionInfo == default; if (needsDbUpdate) { - //needs insertion + // needs insertion revisionInfo = new Models.RevisionInformation { Instance = instance, CommitSha = repoSha, CompileJobs = new List(), - ActiveTestMerges = new List() //non null vals for api returns + ActiveTestMerges = new List() // non null vals for api returns }; - lock (databaseContext) //cleaner this way + lock (databaseContext) // cleaner this way databaseContext.RevisionInformations.Add(revisionInfo); } + revisionInfo.OriginCommitSha = revisionInfo.OriginCommitSha ?? lastOriginCommitSha; if (revisionInfo.OriginCommitSha == null) { revisionInfo.OriginCommitSha = repoSha; Logger.LogWarning(Components.Repository.Repository.OriginTrackingErrorTemplate, repoSha); } + revInfoSink?.Invoke(revisionInfo); return needsDbUpdate; } @@ -112,10 +114,11 @@ namespace Tgstation.Server.Host.Controllers model.GitHubOwner = repository.GitHubOwner; model.GitHubName = repository.GitHubRepoName; } + model.Origin = repository.Origin; model.Reference = repository.Reference; - //rev info stuff + // rev info stuff Models.RevisionInformation revisionInfo = null; var needsDbUpdate = await LoadRevisionInformation(repository, databaseContext, instance, null, x => revisionInfo = x, cancellationToken).ConfigureAwait(false); model.RevisionInformation = revisionInfo.ToApi(); @@ -140,7 +143,7 @@ namespace Tgstation.Server.Host.Controllers if (currentModel == default) return StatusCode((int)HttpStatusCode.Gone); - //normalize github urls + // normalize github urls const string BadGitHubUrl = "://www.github.com/"; var uiOrigin = model.Origin.ToUpperInvariant(); var uiBad = BadGitHubUrl.ToUpperInvariant(); @@ -149,7 +152,7 @@ namespace Tgstation.Server.Host.Controllers model.Origin = uiOrigin.Replace(uiBad, uiGitHub, StringComparison.Ordinal); currentModel.AccessToken = model.AccessToken; - currentModel.AccessUser = model.AccessUser; //intentionally only these fields, user not allowed to change anything else atm + currentModel.AccessUser = model.AccessUser; // intentionally only these fields, user not allowed to change anything else atm var cloneBranch = model.Reference; var origin = model.Origin; @@ -169,8 +172,8 @@ namespace Tgstation.Server.Host.Controllers using (var repo = await repoManager.LoadRepository(cancellationToken).ConfigureAwait(false)) { + // clone conflict if (repo != null) - //clone conflict return Conflict(new ErrorMessage { Message = "The repository already exists!" @@ -269,10 +272,11 @@ namespace Tgstation.Server.Host.Controllers { if (repo != null && await PopulateApi(api, repo, DatabaseContext, Instance, cancellationToken).ConfigureAwait(false)) { - //user may have fucked with the repo without telling us, do what we can + // user may have fucked with the repo without telling us, do what we can await DatabaseContext.Save(cancellationToken).ConfigureAwait(false); return StatusCode((int)HttpStatusCode.Created, api); } + return Json(api); } } @@ -331,7 +335,7 @@ namespace Tgstation.Server.Host.Controllers property.SetValue(currentModel, newVal); return false; - }; + } if (CheckModified(x => x.AccessToken, RepositoryRights.ChangeCredentials) || CheckModified(x => x.AccessUser, RepositoryRights.ChangeCredentials) @@ -346,7 +350,7 @@ namespace Tgstation.Server.Host.Controllers if (currentModel.AccessToken?.Length == 0 && currentModel.AccessUser?.Length == 0) { - //setting an empty string clears everything + // setting an empty string clears everything currentModel.AccessUser = null; currentModel.AccessToken = null; } @@ -380,11 +384,11 @@ namespace Tgstation.Server.Host.Controllers await PopulateApi(api, repo, DatabaseContext, Instance, cancellationToken).ConfigureAwait(false); } } - - //this is just db stuf so stow it away + + // this is just db stuf so stow it away await DatabaseContext.Save(cancellationToken).ConfigureAwait(false); - //format the job description + // format the job description string description = null; if (model.UpdateFromOrigin == true) if (model.Reference != null) @@ -397,16 +401,15 @@ namespace Tgstation.Server.Host.Controllers description = String.Format(CultureInfo.InvariantCulture, "Checkout repository {0} {1}", model.Reference != null ? "reference" : "SHA", model.Reference ?? model.CheckoutSha); if (newTestMerges) - description = String.Format(CultureInfo.InvariantCulture, "{0}est merge pull request(s) {1}{2}", + description = String.Format(CultureInfo.InvariantCulture, "{0}est merge pull request(s) {1}{2}", description != null ? String.Format(CultureInfo.InvariantCulture, "{0} and t", description) : "T", - String.Join(", ", model.NewTestMerges.Select(x => - String.Format(CultureInfo.InvariantCulture, "#{0}{1}", x.Number, + String.Join(", ", model.NewTestMerges.Select(x => + String.Format(CultureInfo.InvariantCulture, "#{0}{1}", x.Number, x.PullRequestRevision != null ? String.Format(CultureInfo.InvariantCulture, " at {0}", x.PullRequestRevision.Substring(0, 7)) : String.Empty))), description != null ? String.Empty : " in repository"); if (description == null) - //no git changes - return Json(api); + return Json(api); // no git changes var job = new Models.Job { @@ -444,14 +447,14 @@ namespace Tgstation.Server.Host.Controllers { var tmpDoneSteps = doneSteps; ++doneSteps; - return progress => progressReporter((progress + 100 * tmpDoneSteps) / numSteps); - }; + return progress => progressReporter((progress + (100 * tmpDoneSteps)) / numSteps); + } progressReporter(0); - //get a base line for where we are + // get a base line for where we are Models.RevisionInformation lastRevisionInfo = null; - + var attachedInstance = new Models.Instance { Id = Instance.Id @@ -460,17 +463,17 @@ namespace Tgstation.Server.Host.Controllers await LoadRevisionInformation(repo, databaseContext, attachedInstance, null, x => lastRevisionInfo = x, ct).ConfigureAwait(false); - //apply new rev info, tracking applied test merges + // apply new rev info, tracking applied test merges async Task UpdateRevInfo() { var last = lastRevisionInfo; await LoadRevisionInformation(repo, databaseContext, attachedInstance, last.OriginCommitSha, x => lastRevisionInfo = x, ct).ConfigureAwait(false); lastRevisionInfo.ActiveTestMerges.AddRange(last.ActiveTestMerges); - }; + } try { - //fetch/pull + // fetch/pull if (model.UpdateFromOrigin == true) { if (!repo.Tracking) @@ -494,7 +497,7 @@ namespace Tgstation.Server.Host.Controllers } } - //checkout/hard reset + // checkout/hard reset if (modelHasShaOrReference) { if ((model.CheckoutSha != null && repo.Head.ToUpperInvariant().StartsWith(model.CheckoutSha.ToUpperInvariant(), StringComparison.Ordinal)) @@ -507,7 +510,7 @@ namespace Tgstation.Server.Host.Controllers throw new JobException("Attempted to checkout a SHA or reference that was actually the opposite!"); await repo.CheckoutObject(committish, NextProgressReporter(), ct).ConfigureAwait(false); - await LoadRevisionInformation(repo, databaseContext, attachedInstance, null, x => lastRevisionInfo = x, ct).ConfigureAwait(false); //we've either seen origin before or what we're checking out is on origin + await LoadRevisionInformation(repo, databaseContext, attachedInstance, null, x => lastRevisionInfo = x, ct).ConfigureAwait(false); // we've either seen origin before or what we're checking out is on origin } else NextProgressReporter()(100); @@ -519,35 +522,36 @@ namespace Tgstation.Server.Host.Controllers await repo.ResetToOrigin(NextProgressReporter(), ct).ConfigureAwait(false); await repo.Sychronize(currentModel.AccessUser, currentModel.AccessToken, currentModel.CommitterName, currentModel.CommitterEmail, NextProgressReporter(), true, ct).ConfigureAwait(false); await LoadRevisionInformation(repo, databaseContext, attachedInstance, null, x => lastRevisionInfo = x, ct).ConfigureAwait(false); - //repo head is on origin so force this - //will update the db if necessary + + // repo head is on origin so force this + // will update the db if necessary lastRevisionInfo.OriginCommitSha = repo.Head; } } + // test merging Dictionary prMap = null; - //test merging if (newTestMerges) { - //bit of sanitization + // bit of sanitization foreach (var I in model.NewTestMerges.Where(x => String.IsNullOrWhiteSpace(x.PullRequestRevision))) I.PullRequestRevision = null; - var gitHubClient = currentModel.AccessToken != null - ? gitHubClientFactory.CreateClient(currentModel.AccessToken) + var gitHubClient = currentModel.AccessToken != null + ? gitHubClientFactory.CreateClient(currentModel.AccessToken) : (String.IsNullOrEmpty(generalConfiguration.GitHubAccessToken) - ? gitHubClientFactory.CreateClient() + ? gitHubClientFactory.CreateClient() : gitHubClientFactory.CreateClient(generalConfiguration.GitHubAccessToken)); var repoOwner = repo.GitHubOwner; var repoName = repo.GitHubRepoName; + // optimization: if we've already merged these exact same commits in this fashion before, just find the rev info for it and check it out Models.RevisionInformation revInfoWereLookingFor = null; bool needToApplyRemainingPrs = true; - //optimization: if we've already merged these exact same commits in this fashion before, just find the rev info for it and check it out if (lastRevisionInfo.OriginCommitSha == lastRevisionInfo.CommitSha) { - //In order for this to work though we need the shas of all the commits + // In order for this to work though we need the shas of all the commits if (model.NewTestMerges.Any(x => x.PullRequestRevision == null)) prMap = new Dictionary(); @@ -555,14 +559,13 @@ namespace Tgstation.Server.Host.Controllers foreach (var I in model.NewTestMerges) { if (I.PullRequestRevision != null) - //normalize the shas to lowercase ala libgit2 #pragma warning disable CA1308 // Normalize strings to uppercase - I.PullRequestRevision = I.PullRequestRevision?.ToLowerInvariant(); + I.PullRequestRevision = I.PullRequestRevision?.ToLowerInvariant(); // ala libgit2 #pragma warning restore CA1308 // Normalize strings to uppercase else - //retrieve the latest sha try { + // retrieve the latest sha var pr = await gitHubClient.PullRequest.Get(repoOwner, repoName, I.Number.Value).ConfigureAwait(false); prMap.Add(I.Number.Value, pr); I.PullRequestRevision = pr.Head.Sha; @@ -585,7 +588,7 @@ namespace Tgstation.Server.Host.Controllers .ThenInclude(x => x.TestMerge) .ToListAsync(cancellationToken).ConfigureAwait(false); - //split here cause this bit has to be done locally + // split here cause this bit has to be done locally revInfoWereLookingFor = dbPull .Where(x => x.ActiveTestMerges.Count == model.NewTestMerges.Count && x.ActiveTestMerges.Select(y => y.TestMerge) @@ -597,7 +600,7 @@ namespace Tgstation.Server.Host.Controllers if (revInfoWereLookingFor == null && model.NewTestMerges.Count > 1) { - //okay try to add at least SOME prs we've seen before + // okay try to add at least SOME prs we've seen before var search = model.NewTestMerges.ToList(); var appliedTestMergeIds = new List(); @@ -623,7 +626,8 @@ namespace Tgstation.Server.Host.Controllers break; } } - } while (revInfoWereLookingFor != null && search.Count > 0); + } + while (revInfoWereLookingFor != null && search.Count > 0); revInfoWereLookingFor = lastGoodRevInfo; needToApplyRemainingPrs = search.Count != 0; @@ -637,14 +641,14 @@ namespace Tgstation.Server.Host.Controllers if (revInfoWereLookingFor != null) { - //goteem + // goteem await repo.ResetToSha(revInfoWereLookingFor.CommitSha, NextProgressReporter(), cancellationToken).ConfigureAwait(false); lastRevisionInfo = revInfoWereLookingFor; } if (needToApplyRemainingPrs) { - //an invocation of LoadRevisionInformation could have already loaded this user + // an invocation of LoadRevisionInformation could have already loaded this user var contextUser = databaseContext.Users.Local.Where(x => x.Id == AuthenticationContext.User.Id).FirstOrDefault(); if (contextUser == default) { @@ -667,13 +671,13 @@ namespace Tgstation.Server.Host.Controllers try { - //load from cache if possible + // load from cache if possible if (prMap == null || !prMap.TryGetValue(I.Number.Value, out pr)) pr = await gitHubClient.PullRequest.Get(repoOwner, repoName, I.Number.Value).ConfigureAwait(false); } catch (Octokit.RateLimitExceededException) { - //you look at your anonymous access and sigh + // you look at your anonymous access and sigh errorMessage = "P.R.E. RATE LIMITED"; } catch (Octokit.AuthorizationException) @@ -682,11 +686,11 @@ namespace Tgstation.Server.Host.Controllers } catch (Octokit.NotFoundException) { - //you look at your shithub and sigh + // you look at your shithub and sigh errorMessage = "P.R.E. NOT FOUND"; } - //we want to take the earliest truth possible to prevent RCEs, if this fails AddTestMerge will set it + // we want to take the earliest truth possible to prevent RCEs, if this fails AddTestMerge will set it if (I.PullRequestRevision == null && pr != null) I.PullRequestRevision = pr.Head.Sha; @@ -729,13 +733,15 @@ namespace Tgstation.Server.Host.Controllers await repo.Sychronize(currentModel.AccessUser, currentModel.AccessToken, currentModel.CommitterName, currentModel.CommitterEmail, NextProgressReporter(), false, ct).ConfigureAwait(false); await UpdateRevInfo().ConfigureAwait(false); } + await databaseContext.Save(ct).ConfigureAwait(false); } catch { doneSteps = 0; numSteps = 2; - //the stuff didn't make it into the db, forget what we've done and abort + + // the stuff didn't make it into the db, forget what we've done and abort await repo.CheckoutObject(startReference ?? startSha, NextProgressReporter(), default).ConfigureAwait(false); if (startReference != null && repo.Head != startSha) await repo.ResetToSha(startSha, NextProgressReporter(), default).ConfigureAwait(false); diff --git a/src/Tgstation.Server.Host/Core/Application.cs b/src/Tgstation.Server.Host/Core/Application.cs index f8fe4f1536..999cb8773e 100644 --- a/src/Tgstation.Server.Host/Core/Application.cs +++ b/src/Tgstation.Server.Host/Core/Application.cs @@ -33,6 +33,7 @@ using Tgstation.Server.Host.Security; namespace Tgstation.Server.Host.Core { /// + #pragma warning disable CA1506 sealed class Application : IApplication { /// diff --git a/src/Tgstation.Server.Host/Core/IJobManager.cs b/src/Tgstation.Server.Host/Core/IJobManager.cs index 12ecf7666f..6599b59a03 100644 --- a/src/Tgstation.Server.Host/Core/IJobManager.cs +++ b/src/Tgstation.Server.Host/Core/IJobManager.cs @@ -12,8 +12,10 @@ namespace Tgstation.Server.Host.Core public interface IJobManager : IHostedService { /// - /// Get the for a job + /// Get the for a /// + /// The to get for + /// The of int? JobProgress(Job job); /// diff --git a/src/Tgstation.Server.Host/Core/IProcessExecutor.cs b/src/Tgstation.Server.Host/Core/IProcessExecutor.cs index 3bd993ada9..57e81ce382 100644 --- a/src/Tgstation.Server.Host/Core/IProcessExecutor.cs +++ b/src/Tgstation.Server.Host/Core/IProcessExecutor.cs @@ -9,8 +9,8 @@ /// Launch a /// /// The full path to the executable file - /// The arguments for the /// The working directory for the + /// The arguments for the /// If standard output should be read /// If standard error should be read /// If shell execute should not be used. Ignored if or are set diff --git a/src/Tgstation.Server.Host/Core/ISetupWizard.cs b/src/Tgstation.Server.Host/Core/ISetupWizard.cs index bf8ddae948..587bcf67b7 100644 --- a/src/Tgstation.Server.Host/Core/ISetupWizard.cs +++ b/src/Tgstation.Server.Host/Core/ISetupWizard.cs @@ -11,8 +11,8 @@ namespace Tgstation.Server.Host.Core /// /// Run the setup wizard if necessary /// - /// - /// + /// The for the operation + /// A resulting in if the wizard ran, otherwise Task CheckRunWizard(CancellationToken cancellationToken); } } diff --git a/src/Tgstation.Server.Host/Core/JobManager.cs b/src/Tgstation.Server.Host/Core/JobManager.cs index bd4d233f9d..dbbc1eba6d 100644 --- a/src/Tgstation.Server.Host/Core/JobManager.cs +++ b/src/Tgstation.Server.Host/Core/JobManager.cs @@ -198,6 +198,7 @@ namespace Tgstation.Server.Host.Core job.Cancelled = true; job.StoppedAt = DateTimeOffset.Now; } + await databaseContext.Save(cancellationToken).ConfigureAwait(false); } }).ConfigureAwait(false); diff --git a/src/Tgstation.Server.Host/Core/Process.cs b/src/Tgstation.Server.Host/Core/Process.cs index 813ea77519..21856ed710 100644 --- a/src/Tgstation.Server.Host/Core/Process.cs +++ b/src/Tgstation.Server.Host/Core/Process.cs @@ -28,6 +28,16 @@ namespace Tgstation.Server.Host.Core /// readonly ILogger logger; + /// + /// Construct a + /// + /// The value of + /// The value of + /// The value of + /// The value of + /// The value of + /// The value of + /// If was NOT just created public Process(System.Diagnostics.Process handle, Task lifetime, StringBuilder outputStringBuilder, StringBuilder errorStringBuilder, StringBuilder combinedStringBuilder, ILogger logger, bool preExisting) { this.handle = handle ?? throw new ArgumentNullException(nameof(handle)); @@ -103,6 +113,7 @@ namespace Tgstation.Server.Host.Core } } + /// public void SetHighPriority() { try diff --git a/src/Tgstation.Server.Host/IO/DefaultIOManager.cs b/src/Tgstation.Server.Host/IO/DefaultIOManager.cs index 6318a7b02a..c303d0a008 100644 --- a/src/Tgstation.Server.Host/IO/DefaultIOManager.cs +++ b/src/Tgstation.Server.Host/IO/DefaultIOManager.cs @@ -180,6 +180,7 @@ namespace Tgstation.Server.Host.IO cancellationToken.ThrowIfCancellationRequested(); results.Add(I); } + return results; }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current); @@ -238,6 +239,7 @@ namespace Tgstation.Server.Host.IO results.Add(I); cancellationToken.ThrowIfCancellationRequested(); } + return (IReadOnlyList)results; }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current); @@ -252,6 +254,7 @@ namespace Tgstation.Server.Host.IO results.Add(I); cancellationToken.ThrowIfCancellationRequested(); } + return (IReadOnlyList)results; }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current); diff --git a/src/Tgstation.Server.Host/IO/IPostWriteHandler.cs b/src/Tgstation.Server.Host/IO/IPostWriteHandler.cs index 7686d742c8..f8c0f4042a 100644 --- a/src/Tgstation.Server.Host/IO/IPostWriteHandler.cs +++ b/src/Tgstation.Server.Host/IO/IPostWriteHandler.cs @@ -1,5 +1,8 @@ namespace Tgstation.Server.Host.IO { + /// + /// Handles changing file modes/permissions after writing + /// interface IPostWriteHandler { /// diff --git a/src/Tgstation.Server.Host/Models/Migrations/20180906135553_MSInitialCreate.cs b/src/Tgstation.Server.Host/Models/Migrations/20180906135553_MSInitialCreate.cs index 57f363fa2a..c796a12444 100644 --- a/src/Tgstation.Server.Host/Models/Migrations/20180906135553_MSInitialCreate.cs +++ b/src/Tgstation.Server.Host/Models/Migrations/20180906135553_MSInitialCreate.cs @@ -7,6 +7,7 @@ namespace Tgstation.Server.Host.Models.Migrations /// /// The initial database migration for MSSQL /// + #pragma warning disable CA1506 public partial class MSInitialCreate : Migration { /// diff --git a/src/Tgstation.Server.Host/Models/Migrations/20180906143029_MYInitialCreate.cs b/src/Tgstation.Server.Host/Models/Migrations/20180906143029_MYInitialCreate.cs index 02e3d313d1..2da9d98878 100644 --- a/src/Tgstation.Server.Host/Models/Migrations/20180906143029_MYInitialCreate.cs +++ b/src/Tgstation.Server.Host/Models/Migrations/20180906143029_MYInitialCreate.cs @@ -7,6 +7,7 @@ namespace Tgstation.Server.Host.Models.Migrations /// /// The initial database migration for MySQL/MariaDB /// + #pragma warning disable CA1506 public partial class MYInitialCreate : Migration { /// diff --git a/src/Tgstation.Server.Host/Models/RepositorySettings.cs b/src/Tgstation.Server.Host/Models/RepositorySettings.cs index 4c11820499..5d040bf81a 100644 --- a/src/Tgstation.Server.Host/Models/RepositorySettings.cs +++ b/src/Tgstation.Server.Host/Models/RepositorySettings.cs @@ -28,7 +28,7 @@ namespace Tgstation.Server.Host.Models /// A new public Repository ToApi() => new Repository { - // AccessToken = AccessToken, //never show this + // AccessToken = AccessToken, // never show this AccessUser = AccessUser, AutoUpdatesKeepTestMerges = AutoUpdatesKeepTestMerges, AutoUpdatesSynchronize = AutoUpdatesSynchronize, diff --git a/src/Tgstation.Server.Host/NativeMethods.cs b/src/Tgstation.Server.Host/NativeMethods.cs index bcc1865686..3686e79f9e 100644 --- a/src/Tgstation.Server.Host/NativeMethods.cs +++ b/src/Tgstation.Server.Host/NativeMethods.cs @@ -7,6 +7,10 @@ namespace Tgstation.Server.Host /// /// Native methods used by the code /// + #pragma warning disable SA1600 + #pragma warning disable SA1602 + #pragma warning disable SA1611 + #pragma warning disable SA1615 static class NativeMethods { /// diff --git a/src/Tgstation.Server.Host/Security/WindowsSystemIdentity.cs b/src/Tgstation.Server.Host/Security/WindowsSystemIdentity.cs index c71544cc35..5125613971 100644 --- a/src/Tgstation.Server.Host/Security/WindowsSystemIdentity.cs +++ b/src/Tgstation.Server.Host/Security/WindowsSystemIdentity.cs @@ -63,8 +63,8 @@ namespace Tgstation.Server.Host.Security { if (identity != null) { - // var newIdentity = (WindowsIdentity)identity.Clone(); //doesn't work because of https://github.com/dotnet/corefx/issues/31841 - var newIdentity = new WindowsIdentity(identity.Token); // the handle is cloned internally + // var newIdentity = (WindowsIdentity)identity.Clone(); //doesn't work because of https://github.com/dotnet/corefx/issues/31841 + var newIdentity = new WindowsIdentity(identity.Token); // the handle is cloned internally return new WindowsSystemIdentity(newIdentity); } diff --git a/src/Tgstation.Server.Host/Security/WindowsSystemIdentityFactory.cs b/src/Tgstation.Server.Host/Security/WindowsSystemIdentityFactory.cs index aec9d8c463..ff4b49c420 100644 --- a/src/Tgstation.Server.Host/Security/WindowsSystemIdentityFactory.cs +++ b/src/Tgstation.Server.Host/Security/WindowsSystemIdentityFactory.cs @@ -77,6 +77,7 @@ namespace Tgstation.Server.Host.Security cancellationToken.ThrowIfCancellationRequested(); } } + return principal != null; } diff --git a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj index 5e7fc42d01..b9f529847e 100644 --- a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj +++ b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj @@ -16,6 +16,7 @@ 1701;1702;SA1652 + D:\tgstation-server\src\Tgstation.Server.Host\.xml