internal static void Run()
        {
            OutboundComms comms = OutboundComms.GetOpen();

            foreach (OutboundComm comm in comms)
            {
                // TODO: RESOLVE RECIPIENTS

                if (!comm.Resolved)
                {
                    continue;
                }

                if (comm.TransmitterClass != "Swarmops.Utility.Communications.CommsTransmitterMail")
                {
                    throw new NotImplementedException();
                }

                ICommsTransmitter transmitter = new CommsTransmitterMail();

                OutboundCommRecipients recipients = comm.Recipients;
                PayloadEnvelope        envelope   = PayloadEnvelope.FromXml(comm.PayloadXml);

                comm.StartTransmission();

                foreach (OutboundCommRecipient recipient in recipients)
                {
                    try
                    {
                        transmitter.Transmit(envelope, recipient.Person);
                        recipient.CloseSuccess();
                    }
                    catch (OutboundCommTransmitException e)
                    {
                        recipient.CloseFailed(e.Description);
                    }
                }

                comm.Open = false;
            }
        }
        internal static void Run()
        {
            OutboundComms comms = OutboundComms.GetOpen();

            foreach (OutboundComm comm in comms)
            {
                BotLog.Write(1, "CommsTx", "OutboundComm #" + comm.Identity.ToString("N0"));

                if (!comm.Resolved)
                {
                    BotLog.Write(2, "CommsTx", "--resolving");

                    ICommsResolver resolver = FindResolver(comm);
                    resolver.Resolve(comm);
                    comm.Resolved = true;

                    int recipientCount = comm.Recipients.Count;
                    BotLog.Write(2, "CommsTx", "--resolved to " + recipientCount.ToString("N0") + " recipients");

                    if (recipientCount > 1 && comm.SenderPersonId != 0)
                    {
                        // "Your message has been queued for delivery and the recipients have been resolved.
                        // Your mail will be sent to, or be attempted to sent to, [RecipientCount] people in [Geography] in [OrganizationName]."

                        NotificationStrings       notifyStrings = new NotificationStrings();
                        NotificationCustomStrings customStrings = new NotificationCustomStrings();
                        notifyStrings[NotificationString.OrganizationName] = Organization.FromIdentity(comm.OrganizationId).Name;
                        customStrings["RecipientCount"] = comm.Recipients.Count.ToString("N0");
                        if (resolver is IHasGeography)
                        {
                            customStrings["GeographyName"] = ((IHasGeography)resolver).Geography.Localized;
                        }
                        OutboundComm.CreateNotification(Organization.FromIdentity(comm.OrganizationId),
                                                        NotificationResource.OutboundComm_Resolved, notifyStrings, customStrings,
                                                        People.FromSingle(Person.FromIdentity(comm.SenderPersonId)));
                    }

                    comm.StartTransmission();

                    continue; // continue is not strictly necessary; could continue processing the same OutboundComm after resolution
                }

                if (comm.TransmitterClass != "Swarmops.Utility.Communications.CommsTransmitterMail")
                {
                    throw new NotImplementedException();
                }

                ICommsTransmitter transmitter = new CommsTransmitterMail();

                const int batchSize = 1000;

                OutboundCommRecipients recipients = comm.GetRecipientBatch(batchSize);
                PayloadEnvelope        envelope   = PayloadEnvelope.FromXml(comm.PayloadXml);

                BotLog.Write(2, "CommsTx", "--transmitting to " + recipients.Count.ToString("N0") + " recipients");

                foreach (OutboundCommRecipient recipient in recipients)
                {
                    try
                    {
                        transmitter.Transmit(envelope, recipient.Person);
                        recipient.CloseSuccess();
                    }
                    catch (OutboundCommTransmitException e)
                    {
                        recipient.CloseFailed(e.Description);
                    }
                }

                if (recipients.Count < batchSize) // Was this the last batch?
                {
                    comm.Open = false;

                    BotLog.Write(2, "CommsTx", "--closing");

                    OutboundComm reloadedComm = OutboundComm.FromIdentity(comm.Identity);
                    // active object doesn't update as we get results, so need to load
                    // from database to get final counts of successes and fails

                    if (comm.RecipientCount > 1 && comm.SenderPersonId != 0)
                    {
                        BotLog.Write(2, "CommsTx", "--notifying");

                        ICommsResolver resolver = FindResolver(comm);

                        // "Your message to [GeographyName] has been sent to all scheduled recipients. Of the [RecipientCount] planned recipients,
                        // [RecipientsSuccess] succeeded from Swarmops' horizon. (These can fail later for a number of reasons, from broken
                        // computers to hospitalized recipients.) Time spent transmitting: [TransmissionTime]."

                        NotificationStrings       notifyStrings = new NotificationStrings();
                        NotificationCustomStrings customStrings = new NotificationCustomStrings();
                        notifyStrings[NotificationString.OrganizationName] =
                            Organization.FromIdentity(comm.OrganizationId).Name;
                        customStrings["RecipientCount"]    = reloadedComm.RecipientCount.ToString("N0");
                        customStrings["RecipientsSuccess"] = reloadedComm.RecipientsSuccess.ToString("N0");

                        TimeSpan resolveTime  = comm.StartTransmitDateTime - comm.CreatedDateTime;
                        TimeSpan transmitTime = comm.ClosedDateTime - comm.StartTransmitDateTime;
                        TimeSpan totalTime    = resolveTime + transmitTime;

                        customStrings["TransmissionTime"] = FormatTimespan(transmitTime);
                        customStrings["ResolvingTime"]    = FormatTimespan(resolveTime);
                        customStrings["TotalTime"]        = FormatTimespan(totalTime);
                        if (resolver is IHasGeography)
                        {
                            customStrings["GeographyName"] = ((IHasGeography)resolver).Geography.Localized;
                        }
                        OutboundComm.CreateNotification(Organization.FromIdentity(comm.OrganizationId),
                                                        NotificationResource.OutboundComm_Sent, notifyStrings, customStrings,
                                                        People.FromSingle(Person.FromIdentity(comm.SenderPersonId)));
                    }
                }
            }
        }
Exemple #3
0
        private static void Main(string[] args)
        {
            testMode = false;

            UnixSignal[] killSignals =
            {
                new UnixSignal(Signum.SIGINT),
                new UnixSignal(Signum.SIGTERM)
            };

            BotLog.Write(0, "MainCycle", string.Empty);
            BotLog.Write(0, "MainCycle", "-----------------------------------------------");
            BotLog.Write(0, "MainCycle", string.Empty);

            if (args.Length > 0)
            {
                if (args[0].ToLower() == "test")
                {
/*
 *                  BotLog.Write(0, "MainCycle", "Running self-tests");
 *                  HeartBeater.Instance.Beat(heartbeatFile);  // Otherwise Heartbeater.Beat() will fail in various places
 *
 *                  testMode = true;
 *                  Console.WriteLine("Testing All Maintenance Processes (except membership-changing ones).");
 *                  PWLog.Write(PWLogItem.None, 0, PWLogAction.SystemTest, string.Empty, string.Empty);
 *
 *                  Console.WriteLine("\r\n10-second intervals:");
 *                  OnEveryTenSeconds();
 *                  Console.WriteLine("\r\nEvery minute:");
 *                  OnEveryMinute();
 *                  Console.WriteLine("\r\nEvery five minutes:");
 *                  OnEveryFiveMinutes();
 *                  Console.WriteLine("\r\nEvery hour:");
 *                  OnEveryHour();
 *                  Console.WriteLine("\r\nNoon:");
 *                  OnNoon();
 *                  Console.WriteLine("\r\nMidnight:");
 *                  OnMidnight();
 */

                    Console.WriteLine("Testing database access...");

                    Console.WriteLine(SwarmDb.GetDatabaseForReading().GetPerson(1).Name);
                    Console.WriteLine(SwarmDb.GetDatabaseForReading().GetPerson(1).PasswordHash);

                    Console.WriteLine("Creating OutboundComm...");

                    OutboundComm.CreateNotification(null, NotificationResource.System_Startup);

                    Console.WriteLine("Transmitting...");

                    OutboundComms comms = OutboundComms.GetOpen();

                    Console.WriteLine("{0} open items in outbound comms.", comms.Count);

                    foreach (OutboundComm comm in comms)
                    {
                        if (comm.TransmitterClass != "Swarmops.Utility.Communications.CommsTransmitterMail")
                        {
                            throw new NotImplementedException();
                        }

                        ICommsTransmitter transmitter = new CommsTransmitterMail();

                        OutboundCommRecipients recipients = comm.Recipients;
                        PayloadEnvelope        envelope   = PayloadEnvelope.FromXml(comm.PayloadXml);

                        foreach (OutboundCommRecipient recipient in recipients)
                        {
                            transmitter.Transmit(envelope, recipient.Person);
                        }
                    }


                    Console.Write("\r\nAll tests run. Waiting for mail queue to flush... ");
                    while (!MailTransmitter.CanExit)
                    {
                        Thread.Sleep(50);
                    }

                    Console.WriteLine("done.");
                    BotLog.Write(0, "MainCycle", "Exiting self-tests");
                    return;
                }

                if (args[0].ToLower() == "console")
                {
                    Console.WriteLine("\r\nRunning Swarmops-Backend in CONSOLE mode.\r\n");

                    // -------------------------------------------------------------------------------------
                    // -------------------------------------------------------------------------------------
                    // -------------------------------------------------------------------------------------

                    // -----------------------    INSERT ANY ONE-OFF ACTIONS HERE  -------------------------


                    Console.Write("\r\nWaiting for mail queue to flush... ");

                    while (!MailTransmitter.CanExit)
                    {
                        Thread.Sleep(50);
                    }

                    Console.WriteLine("done.");

                    return;
                }

                if (args[0].ToLower() == "rsm")
                {
                    Console.WriteLine("Testing character encoding: räksmörgås RÄKSMÖRGÅS");
                    return;
                }
            }

            /*
             * MailMessage message = new MailMessage();
             * message.From = new MailAddress(Strings.MailSenderAddress, Strings.MailSenderName);
             * message.To.Add (new MailAddress ("*****@*****.**", "Rick Falkvinge (Piratpartiet)"));
             * message.Subject = "Räksmörgåsarnas ékÖNÖMÏåvdëlnïng";
             * message.Body = "Hejsan hoppsan Räksmörgåsar.";
             * message.BodyEncoding = Encoding.Default;
             * message.SubjectEncoding = Encoding.Default;
             *
             * SmtpClient smtpClient = new SmtpClient ("localhost");
             * smtpClient.Credentials = null; // mono bug
             * smtpClient.Send (message);*/

            Console.WriteLine(" * Swarmops Backend starting");

            BotLog.Write(0, "MainCycle", "Backend STARTING");

            OutboundComm.CreateNotification(null, NotificationResource.System_Startup);

            // Checking for schemata upgrade

            DatabaseMaintenance.UpgradeSchemata();

            // Check for existence of installation ID. If not, create one. Warning: has privacy implications when communicated.

            if (Persistence.Key["SwarmopsInstallationId"] == string.Empty)
            {
                Persistence.Key["SwarmopsInstallationId"] = Guid.NewGuid().ToString();
            }

            DateTime cycleStartTime = DateTime.Now;

            int lastSecond = cycleStartTime.Second;
            int lastMinute = cycleStartTime.Minute;
            int lastHour   = cycleStartTime.Hour;

            bool exitFlag = false;

            while (!exitFlag) // exit is handled by signals handling at end of loop
            {
                BotLog.Write(0, "MainCycle", "Cycle Start");

                cycleStartTime = DateTime.Now;

                try
                {
                    OnEveryTenSeconds();

                    if (cycleStartTime.Second < lastSecond)
                    {
                        OnEveryMinute();

                        if (cycleStartTime.Minute % 5 == 0)
                        {
                            OnEveryFiveMinutes();
                        }
                    }

                    if (cycleStartTime.Minute < lastMinute)
                    {
                        OnEveryHour();

                        if (DateTime.Now.Hour == 10 && DateTime.Today.DayOfWeek == DayOfWeek.Tuesday)
                        {
                            OnTuesdayMorning();
                        }
                    }

                    if (cycleStartTime.Hour >= 12 && lastHour < 12)
                    {
                        OnNoon();
                    }

                    if (cycleStartTime.Hour < lastHour)
                    {
                        OnMidnight();
                    }
                }

                catch (Exception e)
                {
                    // Note each "OnEvery..." catches its own errors and sends Exception mails,
                    // so that failure in one should not stop the others from running. This particular
                    // code should never run.

                    ExceptionMail.Send(new Exception("Failed in swarmops-backend main loop", e), true);
                }

                lastSecond = cycleStartTime.Second;
                lastMinute = cycleStartTime.Minute;
                lastHour   = cycleStartTime.Hour;

                // Wait for a maximum of ten seconds

                while (DateTime.Now < cycleStartTime.AddSeconds(10) && !exitFlag)
                {
                    // block until a SIGINT or SIGTERM signal is generated, or one second has passed.
                    int signalIndex = UnixSignal.WaitAny(killSignals, 1000);

                    if (signalIndex < 1000)
                    {
                        exitFlag = true;
                        Console.WriteLine("Caught signal " + killSignals[signalIndex].Signum + ", exiting");
                        BotLog.Write(0, "MainCycle",
                                     "EXIT SIGNAL (" + killSignals[signalIndex].Signum + "), terminating backend");
                    }
                }
            }

            Console.WriteLine(" * Swarmops Backend stopping");
            BotLog.Write(0, "MainCycle", "BACKEND EXITING, sending backend-termination notices");

            /*
             * if (HeartBeater.Instance.WasKilled)
             * {
             *  // removed unconditional delete, cron job that restarts bot uses it to know that it is intentionally down.
             *  ExceptionMail.Send(new Exception("HeartBeater triggered restart of Swarmops Backend. Will commence after 800 seconds."), false);
             * }*/

            BotLog.Write(0, "MainCycle", "...done");

            /*
             * while (!MailTransmitter.CanExit)
             * {
             *  System.Threading.Thread.Sleep(50);
             * }*/

            Thread.Sleep(2000);
        }
Exemple #4
0
        private static void Main(string[] args)
        {
            // Are we running yet?

            if (!SystemSettings.DatabaseInitialized)
            {
                // will restart the service every 15s until db initialized on OOBE
                // also, the read of DatabaseInitialized can and will fail if
                // we're not initalized enough to even have a database

                throw new InvalidOperationException();
            }

            // Checking for schemata upgrade first of all, after seeing that db exists

            int startupDbVersion = Database.SwarmDb.DbVersion;

            DatabaseMaintenance.UpgradeSchemata();

            testMode = false;

            SystemSettings.BackendHostname = Dns.GetHostName();


            // Other one-time initializations

            FinancialTransactions.FixAllUnsequenced();
            SupportFunctions.OperatingTopology = OperatingTopology.Backend;

            // Begin main loop

            UnixSignal[] killSignals = null;

            if (!Debugger.IsAttached)
            {
                killSignals = new UnixSignal[] { new UnixSignal(Signum.SIGINT), new UnixSignal(Signum.SIGTERM) };
            }

            BotLog.Write(0, "MainCycle", string.Empty);
            BotLog.Write(0, "MainCycle", "-----------------------------------------------");
            BotLog.Write(0, "MainCycle", string.Empty);

            if (args.Length > 0)
            {
                if (args[0].ToLower() == "test")
                {
/*
 *                  BotLog.Write(0, "MainCycle", "Running self-tests");
 *                  HeartBeater.Instance.Beat(heartbeatFile);  // Otherwise Heartbeater.Beat() will fail in various places
 *
 *                  testMode = true;
 *                  Console.WriteLine("Testing All Maintenance Processes (except membership-changing ones).");
 *                  PWLog.Write(PWLogItem.None, 0, PWLogAction.SystemTest, string.Empty, string.Empty);
 *
 *                  Console.WriteLine("\r\n10-second intervals:");
 *                  OnEveryTenSeconds();
 *                  Console.WriteLine("\r\nEvery minute:");
 *                  OnEveryMinute();
 *                  Console.WriteLine("\r\nEvery five minutes:");
 *                  OnEveryFiveMinutes();
 *                  Console.WriteLine("\r\nEvery hour:");
 *                  OnEveryHour();
 *                  Console.WriteLine("\r\nNoon:");
 *                  OnNoon();
 *                  Console.WriteLine("\r\nMidnight:");
 *                  OnMidnight();
 */

                    Console.WriteLine("Testing database access...");

                    Console.WriteLine(SwarmDb.GetDatabaseForReading().GetPerson(1).Name);
                    Console.WriteLine(SwarmDb.GetDatabaseForReading().GetPerson(1).PasswordHash);

                    Console.WriteLine("Creating OutboundComm...");

                    OutboundComm.CreateNotification(null, NotificationResource.System_Startup_Backend);

                    Console.WriteLine("Transmitting...");

                    OutboundComms comms = OutboundComms.GetOpen();

                    Console.WriteLine("{0} open items in outbound comms.", comms.Count);

                    foreach (OutboundComm comm in comms)
                    {
                        if (comm.TransmitterClass != "Swarmops.Utility.Communications.CommsTransmitterMail")
                        {
                            throw new NotImplementedException();
                        }

                        ICommsTransmitter transmitter = new CommsTransmitterMail();

                        OutboundCommRecipients recipients = comm.Recipients;
                        PayloadEnvelope        envelope   = PayloadEnvelope.FromXml(comm.PayloadXml);

                        foreach (OutboundCommRecipient recipient in recipients)
                        {
                            transmitter.Transmit(envelope, recipient.Person);
                        }
                    }


                    Console.Write("\r\nAll tests run. Waiting for mail queue to flush... ");
                    while (!MailTransmitter.CanExit)
                    {
                        Thread.Sleep(50);
                    }

                    Console.WriteLine("done.");
                    BotLog.Write(0, "MainCycle", "Exiting self-tests");
                    return;
                }

                if (args[0].ToLower() == "console")
                {
                    Console.WriteLine("\r\nRunning Swarmops-Backend in CONSOLE mode.\r\n");

                    // -------------------------------------------------------------------------------------
                    // -------------------------------------------------------------------------------------
                    // -------------------------------------------------------------------------------------

                    // -----------------------    INSERT ANY ONE-OFF ACTIONS HERE  -------------------------


                    Console.Write("\r\nWaiting for mail queue to flush... ");

                    while (!MailTransmitter.CanExit)
                    {
                        Thread.Sleep(50);
                    }

                    Console.WriteLine("done.");

                    return;
                }

                if (args[0].ToLowerInvariant() == "pdfregen")
                {
                    if (args.Length > 1)
                    {
                        int docId = Int32.Parse(args[1]);
                        PdfProcessor.Rerasterize(Document.FromIdentity(docId));
                    }
                    else
                    {
                        Console.WriteLine("Regenerating all bitmaps from PDF uploads.");
                        PdfProcessor.RerasterizeAll();
                        Console.WriteLine("Done.");
                    }

                    return;
                }


                if (args[0].ToLower() == "rsm")
                {
                    Console.WriteLine("Testing character encoding: räksmörgås RÄKSMÖRGÅS");
                    return;
                }
            }

            /*
             * MailMessage message = new MailMessage();
             * message.From = new MailAddress(Strings.MailSenderAddress, Strings.MailSenderName);
             * message.To.Add (new MailAddress ("*****@*****.**", "Rick Falkvinge (Piratpartiet)"));
             * message.Subject = "Räksmörgåsarnas ékÖNÖMÏåvdëlnïng";
             * message.Body = "Hejsan hoppsan Räksmörgåsar.";
             * message.BodyEncoding = Encoding.Default;
             * message.SubjectEncoding = Encoding.Default;
             *
             * SmtpClient smtpClient = new SmtpClient ("localhost");
             * smtpClient.Credentials = null; // mono bug
             * smtpClient.Send (message);*/

            Console.WriteLine(" * Swarmops Backend starting");

            BotLog.Write(0, "MainCycle", "Backend STARTING");

            // Disable certificate checking due to Mono not installing with a certificate repository - this is UTTERLY broken

            SupportFunctions.DisableSslCertificateChecks(); // MONO BUG/MISFEATURE: Mono has no root certificates, so can't verify cert

            // Tell sysop we're starting

            OutboundComm.CreateNotification(null, NotificationResource.System_Startup_Backend);

            // Check for existence of installation ID. If not, create one. Warning: has privacy implications when communicated.

            if (Persistence.Key["SwarmopsInstallationId"] == string.Empty)
            {
                Persistence.Key["SwarmopsInstallationId"] = Guid.NewGuid().ToString();
            }

            // Check for existence of bitcoin hotwallet root

            BitcoinUtility.VerifyBitcoinHotWallet();

            // Initialize backend socket server

            int backendSocketPort = SystemSettings.WebsocketPortBackend;

            _socketServer = new WebSocketServer(backendSocketPort);
            _socketServer.AddWebSocketService <BackendServices>("/Backend");
            _socketServer.Start();

            // Initialize socket client to Blockchain.Info (pending our own services)

            using (
                _blockChainInfoSocket =
                    new WebSocket("wss://ws.blockchain.info/inv?api_code=" + SystemSettings.BlockchainSwarmopsApiKey))
            {
                // Begin maintenance loop

                DateTime cycleStartTime = DateTime.UtcNow;
                DateTime cycleEndTime;

                int lastSecond = cycleStartTime.Second;
                int lastMinute = cycleStartTime.Minute;
                int lastHour   = cycleStartTime.Hour;

                bool exitFlag = false;

                _blockChainInfoSocket.OnOpen    += new EventHandler(OnBlockchainOpen);
                _blockChainInfoSocket.OnError   += new EventHandler <ErrorEventArgs>(OnBlockchainError);
                _blockChainInfoSocket.OnClose   += new EventHandler <CloseEventArgs>(OnBlockchainClose);
                _blockChainInfoSocket.OnMessage += new EventHandler <MessageEventArgs>(OnBlockchainMessage);

                _blockChainInfoSocket.Connect();

                while (!exitFlag) // exit is handled by signals handling at end of loop
                {
                    BotLog.Write(0, "MainCycle", "Cycle Start");

                    cycleStartTime = DateTime.UtcNow;
                    cycleEndTime   = cycleStartTime.AddSeconds(10);

                    try
                    {
                        OnEveryTenSeconds();

                        if (cycleStartTime.Second < lastSecond)
                        {
                            OnEveryMinute();

                            if (cycleStartTime.Minute % 5 == 0)
                            {
                                OnEveryFiveMinutes();
                            }
                        }

                        if (cycleStartTime.Minute < lastMinute)
                        {
                            OnEveryHour();

                            if (DateTime.Now.Hour == 10 && DateTime.Today.DayOfWeek == DayOfWeek.Tuesday)
                            {
                                OnTuesdayMorning();
                            }

                            if (DateTime.Now.Hour == 7 && DateTime.Today.DayOfWeek == DayOfWeek.Monday)
                            {
                                OnMondayMorning();
                            }
                        }

                        if (cycleStartTime.Hour >= 12 && lastHour < 12)
                        {
                            OnNoon();
                        }

                        if (cycleStartTime.Hour < lastHour)
                        {
                            OnMidnight();
                        }
                    }

                    catch (Exception e)
                    {
                        // Note each "OnEvery..." catches its own errors and sends Exception mails,
                        // so that failure in one should not stop the others from running. This particular
                        // code should never run.

                        ExceptionMail.Send(new Exception("Failed in swarmops-backend main loop", e), true);
                    }

                    lastSecond = cycleStartTime.Second;
                    lastMinute = cycleStartTime.Minute;
                    lastHour   = cycleStartTime.Hour;

                    // Wait for a maximum of ten seconds (the difference between cycleStartTime and cycleEndTime)

                    int      iterationCount = 0;
                    DateTime utcNow         = DateTime.UtcNow;
                    while (utcNow < cycleEndTime && !exitFlag)
                    {
                        int signalIndex = 250;

                        // Handle important service orders (those that can't be lost in a random loss
                        // of connection of a socket):

                        BackendServiceOrders backendOrders = BackendServiceOrders.GetNextBatch(5);
                        backendOrders.Execute(); // takes at most 250ms per BSO reqs

                        // Block until a SIGINT or SIGTERM signal is generated, or 1/4 second has passed.
                        // However, we can't do that in a development environment - it won't have the
                        // Mono.Posix assembly, and won't understand UnixSignals. So people running this in
                        // a dev environment will need to stop it manually.

                        if (!Debugger.IsAttached)
                        {
                            signalIndex = UnixSignal.WaitAny(killSignals, 250);
                        }
                        else
                        {
                            TimeSpan timeLeft = (cycleEndTime - utcNow);

                            BotLog.Write(0, "MainCycle Debug",
                                         string.Format(CultureInfo.InvariantCulture,
                                                       "Waiting for {0:F2} more seconds for cycle end",
                                                       timeLeft.TotalMilliseconds / 1000.0));
                            Thread.Sleep(250);
                        }

                        if (signalIndex < 250)
                        {
                            exitFlag = true;
                            Console.WriteLine("Caught signal " + killSignals[signalIndex].Signum + ", exiting");
                            BotLog.Write(0, "MainCycle",
                                         "EXIT SIGNAL (" + killSignals[signalIndex].Signum + "), terminating backend");
                        }

                        utcNow = DateTime.UtcNow;

                        // Every second, send an internal heartbeat

                        if (iterationCount++ % 4 == 0)
                        {
                            InternalHeartbeat();
                        }
                    }
                }
            }

            Console.WriteLine(" * Swarmops Backend stopping");
            BotLog.Write(0, "MainCycle", "BACKEND EXITING, sending backend-termination notices");

            /*
             * if (HeartBeater.Instance.WasKilled)
             * {
             *  // removed unconditional delete, cron job that restarts bot uses it to know that it is intentionally down.
             *  ExceptionMail.Send(new Exception("HeartBeater triggered restart of Swarmops Backend. Will commence after 800 seconds."), false);
             * }*/

            BotLog.Write(0, "MainCycle", "...done");

            /*
             * while (!MailTransmitter.CanExit)
             * {
             *  System.Threading.Thread.Sleep(50);
             * }*/

            _socketServer.Stop();

            Thread.Sleep(2000);
        }