<commit_before>#include <iostream>
#include <string>
#include <unistd.h>
#include <vector>
#include <atomic>
#include <signal.h>

#include <getopt.h>

#include "zcm/zcm-cpp.hpp"
#include "zcm/zcm_coretypes.h"

#include "util/TranscoderPluginDb.hpp"

using namespace std;

static atomic_int done {0};

static void sighandler(int signal)
{
    done++;
    if (done == 3) exit(1);
}

struct Args
{
    vector<string> Achannels;
    vector<string> Bchannels;

    vector<int> Adec;
    vector<int> Bdec;

    string Aurl = "";
    string Burl = "";

    string Aprefix = "";
    string Bprefix = "";

    string plugin_path = "";

    bool   debug = false;

    bool parse(int argc, char *argv[])
    {
        // set some defaults
        const char *optstring = "hA:B:a:b:D:p:d";
        struct option long_opts[] = {
            { "help",              no_argument, 0,  'h' },
            { "A-prefix",    required_argument, 0,   0  },
            { "B-prefix",    required_argument, 0,   0  },
            { "A-endpt",     required_argument, 0,  'A' },
            { "B-endpt",     required_argument, 0,  'B' },
            { "A-channel",   required_argument, 0,  'a' },
            { "B-channel",   required_argument, 0,  'b' },
            { "decimation",  required_argument, 0,  'D' },
            { "plugin-path", required_argument, 0,  'p' },
            { "debug",             no_argument, 0,  'd' },
            { 0, 0, 0, 0 }
        };

        int c;
        vector<int> *currDec = nullptr;
        int option_index;
        while ((c = getopt_long(argc, argv, optstring, long_opts, &option_index)) >= 0) {
            switch (c) {
                case 'A': currDec = nullptr; Aurl = optarg; break;
                case 'B': currDec = nullptr; Burl = optarg; break;
                case 'a':
                    Achannels.push_back(optarg);
                    Adec.push_back(0);
                    currDec = &Adec;
                    break;
                case 'b':
                    Bchannels.push_back(optarg);
                    Bdec.push_back(0);
                    currDec = &Bdec;
                    break;
                case 'D':
                    ZCM_ASSERT(currDec != nullptr &&
                               "Decimation must immediately follow a channel");
                    currDec->back() = atoi(optarg);
                    currDec = nullptr;
                    break;
                case 'p':
                    plugin_path = string(optarg);
                    break;
                case 'd':
                    debug = true;
                    break;
                case 0:
                    if (string(long_opts[option_index].name) == "A-prefix") {
                        currDec = nullptr;
                        Aprefix = optarg;
                    } else if (string(long_opts[option_index].name) == "B-prefix") {
                        currDec = nullptr;
                        Bprefix = optarg;
                    }
                    break;
                case 'h': default: usage(); return false;
            };
        }

        if (Aurl == "") {
            cerr << "Please specify A endpt url with the -A option" << endl;
            return false;
        }

        if (Burl == "") {
            cerr << "Please specify B endpt url with the -B option" << endl;
            return false;
        }

        if (Aurl == Burl) {
            cerr << "A and B endpoint urls must be unique" << endl;
            return false;
        }

        return true;
    }

    void usage()
    {
        cerr << "usage: zcm-bridge [options]" << endl
             << "" << endl
             << "    ZCM bridge utility. Bridges all data from one ZCM network to another." << endl
             << "" << endl
             << "Example:" << endl
             << "    zcm-bridge -A ipc -B udpm://239.255.76.67:7667?ttl=0" << endl
             << "" << endl
             << "Options:" << endl
             << "" << endl
             << "  -h, --help                 Shows this help text and exits" << endl
             << "  -A, --A-enpt=URL           One end of the bridge. Ex: zcm-bridge -A ipc" << endl
             << "  -B, --B-endpt=URL          One end of the bridge. Ex: zcm-bridge -B udpm://239.255.76.67:7667?ttl=0" << endl
             << "      --A-prefix=PREFIX      Specify a prefix for all messages published on the A url" << endl
             << "      --B-prefix=PREFIX      Specify a prefix for all messages published on the B url" << endl
             << "  -a, --A-channel=CHANNEL    One channel to subscribe to on A and repeat to B." << endl
             << "                             This argument can be specified multiple times. If this option is not," << endl
             << "                             present then we subscribe to all messages on the A interface." << endl
             << "                             Ex: zcm-bridge -A ipc -a EXAMPLE -B udpm://239.255.76.67:7667?ttl=0" << endl
             << "  -b, --B-channel=CHANNEL    One channel to subscribe to on B and repeat to A." << endl
             << "                             This argument can be specified multiple times. If this option is not," << endl
             << "                             present then we subscribe to all messages on the B interface." << endl
             << "                             Ex: zcm-bridge -A ipc -B udpm://239.255.76.67:7667?ttl=0 -b EXAMPLE" << endl
             << "  -D, --decimation           Decimation level for the preceeding A-channel or B-channel. " << endl
             << "                             Ex: zcm-bridge -A ipc -B udpm://239.255.76.67:7667?ttl=0 -b EXAMPLE -d 2" << endl
             << "                             This example would result in the message on EXAMPLE being rebroadcast on" << endl
             << "                             the A url every third message." << endl
             << "  -p, --plugin-path=path     Path to shared library containing transcoder plugins" << endl
             << "" << endl << endl;
    }
};

struct Bridge
{
    Args   args;

    zcm::ZCM *zcmA = nullptr;
    zcm::ZCM *zcmB = nullptr;

    static TranscoderPluginDb* pluginDb;
    static vector<zcm::TranscoderPlugin*> plugins;

    struct BridgeInfo
    {
        zcm::ZCM *zcmOut = nullptr;
        string    prefix = "";
        int       decimation = 0;
        int       nSkipped = 0;

        BridgeInfo(zcm::ZCM* zcmOut, const string &prefix, int dec) :
            zcmOut(zcmOut), prefix(prefix), decimation(dec), nSkipped(0) {}

        BridgeInfo() {}
    };

    Bridge() {}

    ~Bridge()
    {
        if (zcmA) delete zcmA;
        if (zcmB) delete zcmB;
        if (pluginDb) { delete pluginDb; pluginDb = nullptr; }
    }

    bool init(int argc, char *argv[])
    {
        if (!args.parse(argc, argv))
            return false;

        // A endpoint network
        zcmA = new zcm::ZCM(args.Aurl);
        if (!zcmA->good()) {
            cerr << "Couldn't initialize A endpoint ZCM network! "
                    "Please check your A endpoint transport url." << endl << endl;
            delete zcmA;
            zcmA = nullptr;
            return false;
        }

        // B endpoint network
        zcmB = new zcm::ZCM(args.Burl);
        if (!zcmB->good()) {
            cerr << "Couldn't initialize B endpoint ZCM network! "
                    "Please check your B endpoint transport url." << endl << endl;
            delete zcmB;
            zcmB = nullptr;
            return false;
        }

        assert(pluginDb == nullptr);
        // Load plugins from path if specified
        if (args.plugin_path != "") {
            pluginDb = new TranscoderPluginDb(args.plugin_path, args.debug);
            vector<const zcm::TranscoderPlugin*> dbPlugins = pluginDb->getPlugins();
            if (dbPlugins.empty()) {
                cerr << "Couldn't find any plugins. Aborting." << endl;
                return false;
            }
            vector<string> dbPluginNames = pluginDb->getPluginNames();
            for (size_t i = 0; i < dbPlugins.size(); ++i) {
                plugins.push_back((zcm::TranscoderPlugin*) dbPlugins[i]);
                if (args.debug) cout << "Loaded plugin: " << dbPluginNames[i] << endl;
            }
        }

        return true;
    }

    static void handler(const zcm::ReceiveBuffer* rbuf, const string& channel, void* usr)
    {
        BridgeInfo* info = (BridgeInfo*)usr;
        if (info->nSkipped++ == info->decimation) {
            info->nSkipped = 0;

            vector<const zcm::LogEvent*> evts;

            zcm::LogEvent le;
            le.timestamp = rbuf->recv_utime;
            le.channel   = channel;
            le.datalen   = rbuf->data_size;
            le.data      = rbuf->data;

            if (!plugins.empty()) {

                int64_t msg_hash;
                __int64_t_decode_array(le.data, 0, 8, &msg_hash, 1);

                for (auto& p : plugins) {
                    vector<const zcm::LogEvent*> pevts =
                        p->transcodeEvent((uint64_t) msg_hash, &le);
                    evts.insert(evts.end(), pevts.begin(), pevts.end());
                }
            }

            if (evts.empty()) evts.push_back(&le);

            for (auto* evt : evts) {
                if (!evt) continue;
                // Must create newChannel in here to handle regex based subscriptions.
                // Ie you cant store the whole channel in BridgeInfo because you
                // don't necessarily know what channel is until you receive a message
                string newChannel = info->prefix + evt->channel;
                info->zcmOut->publish(newChannel, evt->data, evt->datalen);
            }
        }
    }

    void run()
    {
        vector<BridgeInfo> infoA, infoB;

        infoA.reserve(args.Achannels.size());
        infoB.reserve(args.Bchannels.size());

        BridgeInfo defaultA(zcmB, args.Bprefix, 0),
                   defaultB(zcmA, args.Aprefix, 0);

        if (args.Achannels.size() == 0) {
            zcmA->subscribe(".*", &handler, &defaultA);
        } else {
            for (size_t i = 0; i < args.Achannels.size(); i++) {
                infoA.emplace_back(zcmB, args.Bprefix, args.Adec.at(i));
                zcmA->subscribe(args.Achannels.at(i), &handler, &infoA.back());
            }
        }

        if (args.Bchannels.size() == 0) {
            zcmB->subscribe(".*", &handler, &defaultB);
        } else {
            for (size_t i = 0; i < args.Bchannels.size(); i++) {
                infoB.emplace_back(zcmA, args.Aprefix, args.Bdec.at(i));
                zcmB->subscribe(args.Bchannels.at(i), &handler, &infoB.back());
            }
        }

        ZCM_ASSERT(infoA.size() == args.Achannels.size() &&
                   infoA.size() == args.Adec.size() &&
                   infoB.size() == args.Bchannels.size() &&
                   infoB.size() == args.Bdec.size());

        zcmA->start();
        zcmB->start();

        while (!done) usleep(1e6);

        zcmA->stop();
        zcmB->stop();

        zcmA->flush();
        zcmB->flush();
    }
};

TranscoderPluginDb* Bridge::pluginDb = nullptr;
vector<zcm::TranscoderPlugin*> Bridge::plugins = {};

int main(int argc, char *argv[])
{
    Bridge bridge{};
    if (!bridge.init(argc, argv)) return 1;

    // Register signal handlers
    signal(SIGINT, sighandler);
    signal(SIGQUIT, sighandler);
    signal(SIGTERM, sighandler);

    bridge.run();

    cerr << "Bridge exiting" << endl;

    return 0;
}
<commit_msg>Bridge allows same url if prefices are used<commit_after>#include <iostream>
#include <string>
#include <unistd.h>
#include <vector>
#include <atomic>
#include <signal.h>

#include <getopt.h>

#include "zcm/zcm-cpp.hpp"
#include "zcm/zcm_coretypes.h"

#include "util/TranscoderPluginDb.hpp"

using namespace std;

static atomic_int done {0};

static void sighandler(int signal)
{
    done++;
    if (done == 3) exit(1);
}

struct Args
{
    vector<string> Achannels;
    vector<string> Bchannels;

    vector<int> Adec;
    vector<int> Bdec;

    string Aurl = "";
    string Burl = "";

    string Aprefix = "";
    string Bprefix = "";

    string plugin_path = "";

    bool   debug = false;

    bool parse(int argc, char *argv[])
    {
        // set some defaults
        const char *optstring = "hA:B:a:b:D:p:d";
        struct option long_opts[] = {
            { "help",              no_argument, 0,  'h' },
            { "A-prefix",    required_argument, 0,   0  },
            { "B-prefix",    required_argument, 0,   0  },
            { "A-endpt",     required_argument, 0,  'A' },
            { "B-endpt",     required_argument, 0,  'B' },
            { "A-channel",   required_argument, 0,  'a' },
            { "B-channel",   required_argument, 0,  'b' },
            { "decimation",  required_argument, 0,  'D' },
            { "plugin-path", required_argument, 0,  'p' },
            { "debug",             no_argument, 0,  'd' },
            { 0, 0, 0, 0 }
        };

        int c;
        vector<int> *currDec = nullptr;
        int option_index;
        while ((c = getopt_long(argc, argv, optstring, long_opts, &option_index)) >= 0) {
            switch (c) {
                case 'A': currDec = nullptr; Aurl = optarg; break;
                case 'B': currDec = nullptr; Burl = optarg; break;
                case 'a':
                    Achannels.push_back(optarg);
                    Adec.push_back(0);
                    currDec = &Adec;
                    break;
                case 'b':
                    Bchannels.push_back(optarg);
                    Bdec.push_back(0);
                    currDec = &Bdec;
                    break;
                case 'D':
                    ZCM_ASSERT(currDec != nullptr &&
                               "Decimation must immediately follow a channel");
                    currDec->back() = atoi(optarg);
                    currDec = nullptr;
                    break;
                case 'p':
                    plugin_path = string(optarg);
                    break;
                case 'd':
                    debug = true;
                    break;
                case 0:
                    if (string(long_opts[option_index].name) == "A-prefix") {
                        currDec = nullptr;
                        Aprefix = optarg;
                    } else if (string(long_opts[option_index].name) == "B-prefix") {
                        currDec = nullptr;
                        Bprefix = optarg;
                    }
                    break;
                case 'h': default: usage(); return false;
            };
        }

        if (Aurl == "") {
            cerr << "Please specify A endpt url with the -A option" << endl;
            return false;
        }

        if (Burl == "") {
            cerr << "Please specify B endpt url with the -B option" << endl;
            return false;
        }

        if (Aurl == Burl && (Aprefix == "" || Bprefix == "")) {
            cerr << "A and B endpoint urls must be unique if not using prefices" << endl;
            return false;
        }

        return true;
    }

    void usage()
    {
        cerr << "usage: zcm-bridge [options]" << endl
             << "" << endl
             << "    ZCM bridge utility. Bridges all data from one ZCM network to another." << endl
             << "" << endl
             << "Example:" << endl
             << "    zcm-bridge -A ipc -B udpm://239.255.76.67:7667?ttl=0" << endl
             << "" << endl
             << "Options:" << endl
             << "" << endl
             << "  -h, --help                 Shows this help text and exits" << endl
             << "  -A, --A-enpt=URL           One end of the bridge. Ex: zcm-bridge -A ipc" << endl
             << "  -B, --B-endpt=URL          One end of the bridge. Ex: zcm-bridge -B udpm://239.255.76.67:7667?ttl=0" << endl
             << "      --A-prefix=PREFIX      Specify a prefix for all messages published on the A url" << endl
             << "      --B-prefix=PREFIX      Specify a prefix for all messages published on the B url" << endl
             << "  -a, --A-channel=CHANNEL    One channel to subscribe to on A and repeat to B." << endl
             << "                             This argument can be specified multiple times. If this option is not," << endl
             << "                             present then we subscribe to all messages on the A interface." << endl
             << "                             Ex: zcm-bridge -A ipc -a EXAMPLE -B udpm://239.255.76.67:7667?ttl=0" << endl
             << "  -b, --B-channel=CHANNEL    One channel to subscribe to on B and repeat to A." << endl
             << "                             This argument can be specified multiple times. If this option is not," << endl
             << "                             present then we subscribe to all messages on the B interface." << endl
             << "                             Ex: zcm-bridge -A ipc -B udpm://239.255.76.67:7667?ttl=0 -b EXAMPLE" << endl
             << "  -D, --decimation           Decimation level for the preceeding A-channel or B-channel. " << endl
             << "                             Ex: zcm-bridge -A ipc -B udpm://239.255.76.67:7667?ttl=0 -b EXAMPLE -d 2" << endl
             << "                             This example would result in the message on EXAMPLE being rebroadcast on" << endl
             << "                             the A url every third message." << endl
             << "  -p, --plugin-path=path     Path to shared library containing transcoder plugins" << endl
             << "" << endl << endl;
    }
};

struct Bridge
{
    Args   args;

    zcm::ZCM *zcmA = nullptr;
    zcm::ZCM *zcmB = nullptr;

    static TranscoderPluginDb* pluginDb;
    static vector<zcm::TranscoderPlugin*> plugins;

    struct BridgeInfo
    {
        zcm::ZCM *zcmOut = nullptr;
        string    prefix = "";
        int       decimation = 0;
        int       nSkipped = 0;

        BridgeInfo(zcm::ZCM* zcmOut, const string &prefix, int dec) :
            zcmOut(zcmOut), prefix(prefix), decimation(dec), nSkipped(0) {}

        BridgeInfo() {}
    };

    Bridge() {}

    ~Bridge()
    {
        if (zcmA) delete zcmA;
        if (zcmB) delete zcmB;
        if (pluginDb) { delete pluginDb; pluginDb = nullptr; }
    }

    bool init(int argc, char *argv[])
    {
        if (!args.parse(argc, argv))
            return false;

        // A endpoint network
        zcmA = new zcm::ZCM(args.Aurl);
        if (!zcmA->good()) {
            cerr << "Couldn't initialize A endpoint ZCM network! "
                    "Please check your A endpoint transport url." << endl << endl;
            delete zcmA;
            zcmA = nullptr;
            return false;
        }

        // B endpoint network
        zcmB = new zcm::ZCM(args.Burl);
        if (!zcmB->good()) {
            cerr << "Couldn't initialize B endpoint ZCM network! "
                    "Please check your B endpoint transport url." << endl << endl;
            delete zcmB;
            zcmB = nullptr;
            return false;
        }

        assert(pluginDb == nullptr);
        // Load plugins from path if specified
        if (args.plugin_path != "") {
            pluginDb = new TranscoderPluginDb(args.plugin_path, args.debug);
            vector<const zcm::TranscoderPlugin*> dbPlugins = pluginDb->getPlugins();
            if (dbPlugins.empty()) {
                cerr << "Couldn't find any plugins. Aborting." << endl;
                return false;
            }
            vector<string> dbPluginNames = pluginDb->getPluginNames();
            for (size_t i = 0; i < dbPlugins.size(); ++i) {
                plugins.push_back((zcm::TranscoderPlugin*) dbPlugins[i]);
                if (args.debug) cout << "Loaded plugin: " << dbPluginNames[i] << endl;
            }
        }

        return true;
    }

    static void handler(const zcm::ReceiveBuffer* rbuf, const string& channel, void* usr)
    {
        BridgeInfo* info = (BridgeInfo*)usr;
        if (info->nSkipped++ == info->decimation) {
            info->nSkipped = 0;

            vector<const zcm::LogEvent*> evts;

            zcm::LogEvent le;
            le.timestamp = rbuf->recv_utime;
            le.channel   = channel;
            le.datalen   = rbuf->data_size;
            le.data      = rbuf->data;

            if (!plugins.empty()) {

                int64_t msg_hash;
                __int64_t_decode_array(le.data, 0, 8, &msg_hash, 1);

                for (auto& p : plugins) {
                    vector<const zcm::LogEvent*> pevts =
                        p->transcodeEvent((uint64_t) msg_hash, &le);
                    evts.insert(evts.end(), pevts.begin(), pevts.end());
                }
            }

            if (evts.empty()) evts.push_back(&le);

            for (auto* evt : evts) {
                if (!evt) continue;
                // Must create newChannel in here to handle regex based subscriptions.
                // Ie you cant store the whole channel in BridgeInfo because you
                // don't necessarily know what channel is until you receive a message
                string newChannel = info->prefix + evt->channel;
                info->zcmOut->publish(newChannel, evt->data, evt->datalen);
            }
        }
    }

    void run()
    {
        vector<BridgeInfo> infoA, infoB;

        infoA.reserve(args.Achannels.size());
        infoB.reserve(args.Bchannels.size());

        BridgeInfo defaultA(zcmB, args.Bprefix, 0),
                   defaultB(zcmA, args.Aprefix, 0);

        if (args.Achannels.size() == 0) {
            zcmA->subscribe(".*", &handler, &defaultA);
        } else {
            for (size_t i = 0; i < args.Achannels.size(); i++) {
                infoA.emplace_back(zcmB, args.Bprefix, args.Adec.at(i));
                zcmA->subscribe(args.Achannels.at(i), &handler, &infoA.back());
            }
        }

        if (args.Bchannels.size() == 0) {
            zcmB->subscribe(".*", &handler, &defaultB);
        } else {
            for (size_t i = 0; i < args.Bchannels.size(); i++) {
                infoB.emplace_back(zcmA, args.Aprefix, args.Bdec.at(i));
                zcmB->subscribe(args.Bchannels.at(i), &handler, &infoB.back());
            }
        }

        ZCM_ASSERT(infoA.size() == args.Achannels.size() &&
                   infoA.size() == args.Adec.size() &&
                   infoB.size() == args.Bchannels.size() &&
                   infoB.size() == args.Bdec.size());

        zcmA->start();
        zcmB->start();

        while (!done) usleep(1e6);

        zcmA->stop();
        zcmB->stop();

        zcmA->flush();
        zcmB->flush();
    }
};

TranscoderPluginDb* Bridge::pluginDb = nullptr;
vector<zcm::TranscoderPlugin*> Bridge::plugins = {};

int main(int argc, char *argv[])
{
    Bridge bridge{};
    if (!bridge.init(argc, argv)) return 1;

    // Register signal handlers
    signal(SIGINT, sighandler);
    signal(SIGQUIT, sighandler);
    signal(SIGTERM, sighandler);

    bridge.run();

    cerr << "Bridge exiting" << endl;

    return 0;
}
<|endoftext|>


<commit_before>/*
 *
 *  Copyright (c) 2015 Stanislav Zhukov (koncord@rwa.su)
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

#ifndef OPCODE_HPP
#define OPCODE_HPP

#include <cstdint>
#include <vector>

namespace Register
{

    enum
    {
        // GENERAL PURPOSE REGISTERS
        AX = 0, // accumulator register.
        BX, // base address register.
        CX, // count register.
        DX, // data register.

        SI, // source index register.
        DI, // destination index register.

        BP, // base pointer.
        SP, // stack pointer.

        // SEGMENT REGISTERS
        CS, // points at the segment containing the current program.
        DS, // generally points at segment where variables are defined.
        ES, // extra segment register, it's up to a coder to define its usage.
        SS, // points at the segment containing the stack.

        // SPECIAL PURPOSE REGISTERS
        IP, // the instruction pointer.
        FLAGS, // determines the current state of the processor
    };
}
namespace Flag
{

    enum
    {
        Carry = 0,
        Parity = 1,
        Auxiliary = 2,
        Zero = 4,
        Sign = 8,
        Trap = 16,
        Interrupt = 32,
        Direction = 64,
        Overflow = 128
    };

    inline uint16_t Begin()
    {
        return Carry;
    }

    inline uint16_t End()
    {
        return Overflow;
    }
}

struct Operand
{

    enum MOD
    {
        Empty = 0,
        Register = 1,
        Memory = 2,
        Immediate = 3,
    };
    enum EXTRA
    {
        Near = 0,
        Short = 1,
        Far = 2,
    };

    void serialization(std::vector<uint8_t> *serial) const
    {
        serial->push_back(mod);
        serial->push_back(data);
    }
    uint8_t mod;
    uint16_t data;
};

namespace Opcode
{
    enum
    {
        ADD = 0x00, // ADD r/m, r/m
        MOV, // MOV r/m, r/m (m to m not allowed)
        SUB,
        CMP,
        AND,
        XCHG,
        TEST,
        OR,
        XOR,
        SHL,
        ADC,
        SBB,
        SAL = SHL,
        SHR,
        SAR,
        IMUL2,

        JMP,
        JNZ,
        JNE = JNZ,
        JZ,
        JE = JZ,
        JC,
        JNAE = JC,
        JB = JC,
        JNC,
        JAE = JNC,
        JNB = JNC,
        JS,
        JNS,
        JO,
        JNO,
        JA,
        JNBE = JA,
        JNA,
        JBE = JNA,
        JP,
        JNP,
        JG,
        JNLE = JG,
        JGE,
        JNL = JGE,
        JL,
        JNGE = JL,
        JLE,
        JNG = JLE,
        JCXZ,
        JECXZ=JCXZ,
        NOT,
        NEG,
        CALL,
        PUSH,
        POP,
        MUL,
        DIV,
        LOOP,
        LOOPNZ,
        LOOPZ,
        RET,    // hack =.
        RETN=RET,
        RETF=RET,
        IRET,
        INC,
        IDIV,
        IMUL1,
        INT,
        DEC,

        HLT,
        CLC,
        CLD,
        CLI,
        CMC,
        STC,
        STD,
        STI,
        PUSHA,
        POPA,
        CWD,
        INT3,
        NOP,

        IMUL3,
    };

    inline uint8_t Args(int opcode)
    {
        if(opcode >= JMP && opcode <= DEC)
            return 1;
        if(opcode >= HLT && opcode <= NOP)
            return 0;
        else if(opcode == IMUL3)
            return 3;
        else
            return 2;
    }
}

#endif /* OPCODE_HPP */
<commit_msg>Components: Give name to Opcode's enum<commit_after>/*
 *
 *  Copyright (c) 2015-2017 Stanislav Zhukov
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

#ifndef OPCODE_HPP
#define OPCODE_HPP

#include <cstdint>
#include <vector>

namespace Register
{

    enum
    {
        // GENERAL PURPOSE REGISTERS
        AX = 0, // accumulator register.
        BX, // base address register.
        CX, // count register.
        DX, // data register.

        SI, // source index register.
        DI, // destination index register.

        BP, // base pointer.
        SP, // stack pointer.

        // SEGMENT REGISTERS
        CS, // points at the segment containing the current program.
        DS, // generally points at segment where variables are defined.
        ES, // extra segment register, it's up to a coder to define its usage.
        SS, // points at the segment containing the stack.

        // SPECIAL PURPOSE REGISTERS
        IP,    // the instruction pointer.
        FLAGS, // determines the current state of the processor
    };
}
namespace Flag
{

    enum
    {
        Carry = 0,
        Parity = 1,
        Auxiliary = 2,
        Zero = 4,
        Sign = 8,
        Trap = 16,
        Interrupt = 32,
        Direction = 64,
        Overflow = 128
    };

    inline uint16_t Begin()
    {
        return Carry;
    }

    inline uint16_t End()
    {
        return Overflow;
    }
}

struct Operand
{

    enum MOD
    {
        Empty = 0,
        Register = 1,
        Memory = 2,
        Immediate = 3,
    };
    enum EXTRA
    {
        Near = 0,
        Short = 1,
        Far = 2,
    };

    void serialization(std::vector<uint8_t> *serial) const
    {
        serial->push_back(mod);
        serial->push_back(data);
    }
    uint8_t mod;
    uint16_t data;
};

namespace Opcode
{
    enum Opcode
    {
/****** 2 args ******/
        ADD = 0x00, // ADD r/m, r/m
        MOV, // MOV r/m, r/m (m to m not allowed)
        SUB,
        CMP,
        AND,
        XCHG,
        TEST,
        OR,
        XOR,
        SHL,
        ADC,
        SBB,
        SAL = SHL,
        SHR,
        SAR,
        IMUL2,

/****** 1 args ******/
        JMP,
        JNZ,
        JNE = JNZ,
        JZ,
        JE = JZ,
        JC,
        JNAE = JC,
        JB = JC,
        JNC,
        JAE = JNC,
        JNB = JNC,
        JS,
        JNS,
        JO,
        JNO,
        JA,
        JNBE = JA,
        JNA,
        JBE = JNA,
        JP,
        JNP,
        JG,
        JNLE = JG,
        JGE,
        JNL = JGE,
        JL,
        JNGE = JL,
        JLE,
        JNG = JLE,
        JCXZ,
        JECXZ=JCXZ,
        NOT,
        NEG,
        CALL,
        PUSH,
        POP,
        MUL,
        DIV,
        LOOP,
        LOOPNZ,
        LOOPZ,
        RET,    // hack =.
        RETN=RET,
        RETF=RET,
        IRET,
        INC,
        IDIV,
        IMUL1,
        INT,
        DEC,

/****** 0 args ******/
        HLT,
        CLC,
        CLD,
        CLI,
        CMC,
        STC,
        STD,
        STI,
        PUSHA,
        POPA,
        CWD,
        INT3,
        NOP,

/****** 3 args ******/
        IMUL3
    };

    inline uint8_t Args(int opcode)
    {
        if(opcode >= JMP && opcode <= DEC)
            return 1;
        if(opcode >= HLT && opcode <= NOP)
            return 0;
        else if(opcode == IMUL3)
            return 3;
        else
            return 2;
    }
}

#endif /* OPCODE_HPP */
<|endoftext|>


<commit_before>/*
===========================================================================

daemon gpl source code
copyright (c) 2013 unvanquished developers

this file is part of the daemon gpl source code (daemon source code).

daemon source code is free software: you can redistribute it and/or modify
it under the terms of the gnu general public license as published by
the free software foundation, either version 3 of the license, or
(at your option) any later version.

daemon source code is distributed in the hope that it will be useful,
but without any warranty; without even the implied warranty of
merchantability or fitness for a particular purpose.  see the
gnu general public license for more details.

you should have received a copy of the gnu general public license
along with daemon source code.  if not, see <http://www.gnu.org/licenses/>.

===========================================================================
*/

#include "AudioPrivate.h"

namespace Audio {

    Log::Logger audioLogs("audio");

    static Cvar::Cvar<float> masterVolume("sound.volume.master", "the global audio volume", Cvar::ARCHIVE, 0.8f);

    static Cvar::Cvar<bool> muteWhenMinimized("sound.muteWhenMinimized", "should the game be muted when minimized", Cvar::ARCHIVE, false);
    static Cvar::Cvar<bool> muteWhenUnfocused("sound.muteWhenUnfocused", "should the game be muted when not focused", Cvar::ARCHIVE, false);

    //TODO make them the equivalent of LATCH and ROM for available*
    static Cvar::Cvar<std::string> deviceString("s_alDevice", "the OpenAL device to use", Cvar::ARCHIVE, "");
    static Cvar::Cvar<std::string> availableDevices("s_alAvailableDevices", "the available OpenAL devices", 0, "");

    static Cvar::Cvar<std::string> inputDeviceString("s_alInputDevice", "the OpenAL input device to use", Cvar::ARCHIVE, "");
    static Cvar::Cvar<std::string> availableInputDevices("s_alAvailableInputDevices", "the available input OpenAL devices", 0, "");

    // We mimic the behavior of the previous sound system by allowing only one looping ousnd per entity.
    // (and only one entities) CGame will add at each frame all the loops: if a loop hasn't been given
    // in a frame, it means it sould be destroyed.
    struct entityLoop_t {
        bool addedThisFrame;
        std::shared_ptr<LoopingSound> sound;
        sfxHandle_t newSfx;
        sfxHandle_t oldSfx;
    };
    static entityLoop_t entityLoops[MAX_GENTITIES];

    static std::shared_ptr<StreamingSound> streams[N_STREAMS];

    static bool initialized = false;

    static AL::Device* device;
    static AL::Context* context;

    // Like in the previous sound system, we only have a single music
    std::shared_ptr<MusicSound> music;

    bool IsValidEntity(int entityNum) {
        return entityNum >= 0 and entityNum < MAX_GENTITIES;
    }

    bool IsValidVector(const vec3_t v) {
        return not std::isnan(v[0]) and not std::isnan(v[1]) and not std::isnan(v[2]);
    }

    bool Init() {
        if (initialized) {
            return true;
        }

        // Initializes a device
        std::string deviceToTry = deviceString.Get();
        if (not deviceToTry.empty()) {
            device = AL::Device::FromName(deviceToTry);

            if (not device) {
                Log::Warn("Could not open OpenAL device '%s', trying the default device.", deviceToTry);
                device = AL::Device::GetDefaultDevice();
            }
        } else {
            device = AL::Device::GetDefaultDevice();
        }

        if (not device) {
            Log::Warn("Could not open an OpenAL device.");
            return false;
        }

        // Initializes a context
        context = AL::Context::GetDefaultContext(*device);

        if (not context) {
            delete device;
            device = nullptr;
            Log::Warn("Could not create an OpenAL context.");
            return false;
        }

        std::stringstream deviceList;
        for (auto deviceName : AL::Device::ListByName()) {
            deviceList << deviceName << "\n";
        }
        availableDevices.Set(deviceList.str());

        context->MakeCurrent();

        // Initializes the list of input devices

        std::stringstream inputDeviceList;
        for (auto inputDeviceName : AL::CaptureDevice::ListByName()) {
            inputDeviceList << inputDeviceName << "\n";
        }
        availableInputDevices.Set(inputDeviceList.str());

        audioLogs.Notice(AL::GetSystemInfo(device, nullptr));

        initialized = true;

        // Initializes the rest of the audio system
        InitSamples();
        InitSounds();
        InitEmitters();

        for (int i = 0; i < MAX_GENTITIES; i++) {
            entityLoops[i] = {false, nullptr, -1, -1};
        }

        return true;
    }

    void Shutdown() {
        if (not initialized) {
            return;
        }

        // Shuts down the wrapper
        for (int i = 0; i < MAX_GENTITIES; i++) {
            if (entityLoops[i].sound) {
                entityLoops[i].sound->Stop();
            }
            entityLoops[i] = {false, nullptr, -1, -1};
        }

        StopMusic();
        StopCapture();

        // Shuts down the rest of the system
        // Shutdown sounds before emitters because they are using a effects defined in emitters
        // and that OpenAL checks the refcount before deleting the object.
        ShutdownSounds();
        ShutdownEmitters();
        ShutdownSamples();

        // Free OpenAL resources
        delete context;
        delete device;

        context = nullptr;
        device = nullptr;

        initialized = false;
    }

    void Update() {
        if (not initialized) {
            return;
        }

        for (int i = 0; i < MAX_GENTITIES; i++) {
            auto& loop = entityLoops[i];
            if (loop.sound and not loop.addedThisFrame) {
                // The loop wasn't added this frame, that means it has to be removed.
                loop.sound->FadeOutAndDie();
                loop = {false, nullptr, -1, -1};

            } else if (loop.oldSfx != loop.newSfx) {
                // The last sfx added in the frame is not the current one being played
                // To mimic the previous sound system's behavior we sart playing the new one.
                loop.sound->FadeOutAndDie();

                int newSfx = loop.newSfx;
                loop = {false, nullptr, -1, -1};

                AddEntityLoopingSound(i, newSfx);
            }
        }

        if ((muteWhenMinimized.Get() and com_minimized->integer) or (muteWhenUnfocused.Get() and com_unfocused->integer)) {
            AL::SetListenerGain(0.0f);
        } else {
            AL::SetListenerGain(masterVolume.Get());
        }

        // Update the rest of the system
        UpdateEmitters();
        UpdateSounds();

        for (int i = 0; i < MAX_GENTITIES; i++) {
            entityLoops[i].addedThisFrame = false;
            // if we are the unique owner of a loop pointer, then it means it was stopped, free it.
            if (entityLoops[i].sound.unique()) {
                entityLoops[i] = {false, nullptr, -1, -1};
            }
        }

        for (int i = 0; i < N_STREAMS; i++) {
            if (streams[i] and streams[i].unique()) {
                streams[i] = nullptr;
            }
        }
    }

    sfxHandle_t RegisterSFX(Str::StringRef filename) {
        // TODO: what should we do if we aren't initialized?
        return RegisterSample(filename)->GetHandle();
    }

    void StartSound(int entityNum, const vec3_t origin, sfxHandle_t sfx) {
        if (not initialized or not Sample::IsValidHandle(sfx)) {
            return;
        }

        std::shared_ptr<Emitter> emitter;

        // Apparently no origin means it is attached to an entity
        if (not origin and IsValidEntity(entityNum)) {
            emitter = GetEmitterForEntity(entityNum);

        } else if(origin and IsValidVector(origin)){
            emitter = GetEmitterForPosition(origin);

        } else {
            return;
        }

        AddSound(emitter, std::make_shared<OneShotSound>(Sample::FromHandle(sfx)), 1);
    }

    void StartLocalSound(sfxHandle_t sfx) {
        if (not initialized or not Sample::IsValidHandle(sfx)) {
            return;
        }

        AddSound(GetLocalEmitter(), std::make_shared<OneShotSound>(Sample::FromHandle(sfx)), 1);
    }

    void AddEntityLoopingSound(int entityNum, sfxHandle_t sfx) {
        if (not initialized or not Sample::IsValidHandle(sfx) or not IsValidEntity(entityNum)) {
            return;
        }

        entityLoop_t& loop = entityLoops[entityNum];

        // If we have no sound we can play the loop directly
        if (not loop.sound) {
            loop.sound = std::make_shared<LoopingSound>(Sample::FromHandle(sfx));
            loop.oldSfx = sfx;
            AddSound(GetEmitterForEntity(entityNum), loop.sound, 1);
        }
        loop.addedThisFrame = true;

        // We remember what is the last sfx asked because cgame expects the sfx added last in the frame to be played
        loop.newSfx = sfx;
    }

    void ClearAllLoopingSounds() {
        if (not initialized) {
            return;
        }

        for (int i = 0; i < MAX_GENTITIES; i++) {
            ClearLoopingSoundsForEntity(i);
        }
    }

    void ClearLoopingSoundsForEntity(int entityNum) {
        if (not initialized or not IsValidEntity(entityNum)) {
            return;
        }

        if (entityLoops[entityNum].sound) {
            entityLoops[entityNum].addedThisFrame = false;
        }
    }

    void StartMusic(Str::StringRef leadingSound, Str::StringRef loopSound) {
        if (not initialized) {
            return;
        }

        StopMusic();
        music = std::make_shared<MusicSound>(leadingSound, loopSound);
        AddSound(GetLocalEmitter(), music, 1);
    }

    void StopMusic() {
        if (not initialized) {
            return;
        }

        if (music) {
            music->Stop();
        }
        music = nullptr;
    }

    void StreamData(int streamNum, const void* data, int numSamples, int rate, int width, float volume, int entityNum) {
        if (not initialized or (streamNum < 0 or streamNum >= N_STREAMS)) {
            return;
        }

        if (not streams[streamNum]) {
            streams[streamNum] = std::make_shared<StreamingSound>();
            if (IsValidEntity(entityNum)) {
                AddSound(GetLocalEmitter(), streams[streamNum], 1);
            } else {
                AddSound(GetEmitterForEntity(entityNum), streams[streamNum], 1);
            }
        }

        streams[streamNum]->SetGain(volume);

        snd_info_t dataInfo = {rate, width, 1, numSamples, (width * numSamples), 0};
        AL::Buffer buffer;

        int feedError = buffer.Feed(dataInfo, data);

        if (not feedError) {
            streams[streamNum]->AppendBuffer(std::move(buffer));
        }
    }

    void UpdateListener(int entityNum, const vec3_t orientation[3]) {
        if (not initialized or
            not IsValidEntity(entityNum) or
            not orientation or
            not IsValidVector(orientation[0]) or
            not IsValidVector(orientation[1]) or
            not IsValidVector(orientation[2])) {
            return;
        }

        UpdateListenerEntity(entityNum, orientation);
    }

    void UpdateEntityPosition(int entityNum, const vec3_t position) {
        if (not initialized or not IsValidEntity(entityNum) or not IsValidVector(position)) {
            return;
        }

        UpdateRegisteredEntityPosition(entityNum, position);
    }

    void UpdateEntityVelocity(int entityNum, const vec3_t velocity) {
        if (not initialized or not IsValidEntity(entityNum) or not IsValidVector(velocity)) {
            return;
        }

        UpdateRegisteredEntityVelocity(entityNum, velocity);
    }

    void UpdateEntityOcclusion(int entityNum, float ratio) {
        if (not initialized or not IsValidEntity(entityNum) or std::isnan(ratio)) {
            return;
        }

        UpdateRegisteredEntityOcclusion(entityNum, ratio);
    }

    // Capture functions

    static AL::CaptureDevice* capture = nullptr;

    void StartCapture(int rate) {
        if (capture or not initialized) {
            return;
        }

        capture = AL::CaptureDevice::GetDefaultDevice(rate);
        capture->Start();
    }

    int AvailableCaptureSamples() {
        if (not capture) {
            return 0;
        }

        return capture->GetNumCapturedSamples();
    }

    void GetCapturedData(int numSamples, void* buffer) {
        if (not capture) {
            return;
        }

        capture->Capture(numSamples, buffer);
    }

    void StopCapture() {
        if (not initialized) {
            return;
        }

        if (capture) {
            capture->Stop();
        }
        delete capture;
        capture = nullptr;
    }

    // Console commands

    class ALInfoCmd : public Cmd::StaticCmd {
        public:
            ALInfoCmd(): StaticCmd("printALInfo", Cmd::AUDIO, N_("Prints information about OpenAL")) {
            }

            virtual void Run(const Cmd::Args&) const OVERRIDE {
                Print(AL::GetSystemInfo(device, capture));
            }
    };
    static ALInfoCmd alInfoRegistration;

    class ListSamplesCmd : public Cmd::StaticCmd {
        public:
            ListSamplesCmd(): StaticCmd("listAudioSamples", Cmd::AUDIO, N_("Lists all the loaded sound samples")) {
            }

            virtual void Run(const Cmd::Args&) const OVERRIDE {
                std::vector<std::string> samples = ListSamples();

                std::sort(samples.begin(), samples.end());

                for (auto& sample: samples) {
                    Print(sample);
                }
                Print("%i samples", samples.size());
            }
    };
    static ListSamplesCmd listSamplesRegistration;

}
<commit_msg>Audio: add the /stopSounds command<commit_after>/*
===========================================================================

daemon gpl source code
copyright (c) 2013 unvanquished developers

this file is part of the daemon gpl source code (daemon source code).

daemon source code is free software: you can redistribute it and/or modify
it under the terms of the gnu general public license as published by
the free software foundation, either version 3 of the license, or
(at your option) any later version.

daemon source code is distributed in the hope that it will be useful,
but without any warranty; without even the implied warranty of
merchantability or fitness for a particular purpose.  see the
gnu general public license for more details.

you should have received a copy of the gnu general public license
along with daemon source code.  if not, see <http://www.gnu.org/licenses/>.

===========================================================================
*/

#include "AudioPrivate.h"

namespace Audio {

    Log::Logger audioLogs("audio");

    static Cvar::Cvar<float> masterVolume("sound.volume.master", "the global audio volume", Cvar::ARCHIVE, 0.8f);

    static Cvar::Cvar<bool> muteWhenMinimized("sound.muteWhenMinimized", "should the game be muted when minimized", Cvar::ARCHIVE, false);
    static Cvar::Cvar<bool> muteWhenUnfocused("sound.muteWhenUnfocused", "should the game be muted when not focused", Cvar::ARCHIVE, false);

    //TODO make them the equivalent of LATCH and ROM for available*
    static Cvar::Cvar<std::string> deviceString("s_alDevice", "the OpenAL device to use", Cvar::ARCHIVE, "");
    static Cvar::Cvar<std::string> availableDevices("s_alAvailableDevices", "the available OpenAL devices", 0, "");

    static Cvar::Cvar<std::string> inputDeviceString("s_alInputDevice", "the OpenAL input device to use", Cvar::ARCHIVE, "");
    static Cvar::Cvar<std::string> availableInputDevices("s_alAvailableInputDevices", "the available input OpenAL devices", 0, "");

    // We mimic the behavior of the previous sound system by allowing only one looping ousnd per entity.
    // (and only one entities) CGame will add at each frame all the loops: if a loop hasn't been given
    // in a frame, it means it sould be destroyed.
    struct entityLoop_t {
        bool addedThisFrame;
        std::shared_ptr<LoopingSound> sound;
        sfxHandle_t newSfx;
        sfxHandle_t oldSfx;
    };
    static entityLoop_t entityLoops[MAX_GENTITIES];

    static std::shared_ptr<StreamingSound> streams[N_STREAMS];

    static bool initialized = false;

    static AL::Device* device;
    static AL::Context* context;

    // Like in the previous sound system, we only have a single music
    std::shared_ptr<MusicSound> music;

    bool IsValidEntity(int entityNum) {
        return entityNum >= 0 and entityNum < MAX_GENTITIES;
    }

    bool IsValidVector(const vec3_t v) {
        return not std::isnan(v[0]) and not std::isnan(v[1]) and not std::isnan(v[2]);
    }

    bool Init() {
        if (initialized) {
            return true;
        }

        // Initializes a device
        std::string deviceToTry = deviceString.Get();
        if (not deviceToTry.empty()) {
            device = AL::Device::FromName(deviceToTry);

            if (not device) {
                Log::Warn("Could not open OpenAL device '%s', trying the default device.", deviceToTry);
                device = AL::Device::GetDefaultDevice();
            }
        } else {
            device = AL::Device::GetDefaultDevice();
        }

        if (not device) {
            Log::Warn("Could not open an OpenAL device.");
            return false;
        }

        // Initializes a context
        context = AL::Context::GetDefaultContext(*device);

        if (not context) {
            delete device;
            device = nullptr;
            Log::Warn("Could not create an OpenAL context.");
            return false;
        }

        std::stringstream deviceList;
        for (auto deviceName : AL::Device::ListByName()) {
            deviceList << deviceName << "\n";
        }
        availableDevices.Set(deviceList.str());

        context->MakeCurrent();

        // Initializes the list of input devices

        std::stringstream inputDeviceList;
        for (auto inputDeviceName : AL::CaptureDevice::ListByName()) {
            inputDeviceList << inputDeviceName << "\n";
        }
        availableInputDevices.Set(inputDeviceList.str());

        audioLogs.Notice(AL::GetSystemInfo(device, nullptr));

        initialized = true;

        // Initializes the rest of the audio system
        InitSamples();
        InitSounds();
        InitEmitters();

        for (int i = 0; i < MAX_GENTITIES; i++) {
            entityLoops[i] = {false, nullptr, -1, -1};
        }

        return true;
    }

    void Shutdown() {
        if (not initialized) {
            return;
        }

        // Shuts down the wrapper
        for (int i = 0; i < MAX_GENTITIES; i++) {
            if (entityLoops[i].sound) {
                entityLoops[i].sound->Stop();
            }
            entityLoops[i] = {false, nullptr, -1, -1};
        }

        StopMusic();
        StopCapture();

        // Shuts down the rest of the system
        // Shutdown sounds before emitters because they are using a effects defined in emitters
        // and that OpenAL checks the refcount before deleting the object.
        ShutdownSounds();
        ShutdownEmitters();
        ShutdownSamples();

        // Free OpenAL resources
        delete context;
        delete device;

        context = nullptr;
        device = nullptr;

        initialized = false;
    }

    void Update() {
        if (not initialized) {
            return;
        }

        for (int i = 0; i < MAX_GENTITIES; i++) {
            auto& loop = entityLoops[i];
            if (loop.sound and not loop.addedThisFrame) {
                // The loop wasn't added this frame, that means it has to be removed.
                loop.sound->FadeOutAndDie();
                loop = {false, nullptr, -1, -1};

            } else if (loop.oldSfx != loop.newSfx) {
                // The last sfx added in the frame is not the current one being played
                // To mimic the previous sound system's behavior we sart playing the new one.
                loop.sound->FadeOutAndDie();

                int newSfx = loop.newSfx;
                loop = {false, nullptr, -1, -1};

                AddEntityLoopingSound(i, newSfx);
            }
        }

        if ((muteWhenMinimized.Get() and com_minimized->integer) or (muteWhenUnfocused.Get() and com_unfocused->integer)) {
            AL::SetListenerGain(0.0f);
        } else {
            AL::SetListenerGain(masterVolume.Get());
        }

        // Update the rest of the system
        UpdateEmitters();
        UpdateSounds();

        for (int i = 0; i < MAX_GENTITIES; i++) {
            entityLoops[i].addedThisFrame = false;
            // if we are the unique owner of a loop pointer, then it means it was stopped, free it.
            if (entityLoops[i].sound.unique()) {
                entityLoops[i] = {false, nullptr, -1, -1};
            }
        }

        for (int i = 0; i < N_STREAMS; i++) {
            if (streams[i] and streams[i].unique()) {
                streams[i] = nullptr;
            }
        }
    }

    sfxHandle_t RegisterSFX(Str::StringRef filename) {
        // TODO: what should we do if we aren't initialized?
        return RegisterSample(filename)->GetHandle();
    }

    void StartSound(int entityNum, const vec3_t origin, sfxHandle_t sfx) {
        if (not initialized or not Sample::IsValidHandle(sfx)) {
            return;
        }

        std::shared_ptr<Emitter> emitter;

        // Apparently no origin means it is attached to an entity
        if (not origin and IsValidEntity(entityNum)) {
            emitter = GetEmitterForEntity(entityNum);

        } else if(origin and IsValidVector(origin)){
            emitter = GetEmitterForPosition(origin);

        } else {
            return;
        }

        AddSound(emitter, std::make_shared<OneShotSound>(Sample::FromHandle(sfx)), 1);
    }

    void StartLocalSound(sfxHandle_t sfx) {
        if (not initialized or not Sample::IsValidHandle(sfx)) {
            return;
        }

        AddSound(GetLocalEmitter(), std::make_shared<OneShotSound>(Sample::FromHandle(sfx)), 1);
    }

    void AddEntityLoopingSound(int entityNum, sfxHandle_t sfx) {
        if (not initialized or not Sample::IsValidHandle(sfx) or not IsValidEntity(entityNum)) {
            return;
        }

        entityLoop_t& loop = entityLoops[entityNum];

        // If we have no sound we can play the loop directly
        if (not loop.sound) {
            loop.sound = std::make_shared<LoopingSound>(Sample::FromHandle(sfx));
            loop.oldSfx = sfx;
            AddSound(GetEmitterForEntity(entityNum), loop.sound, 1);
        }
        loop.addedThisFrame = true;

        // We remember what is the last sfx asked because cgame expects the sfx added last in the frame to be played
        loop.newSfx = sfx;
    }

    void ClearAllLoopingSounds() {
        if (not initialized) {
            return;
        }

        for (int i = 0; i < MAX_GENTITIES; i++) {
            ClearLoopingSoundsForEntity(i);
        }
    }

    void ClearLoopingSoundsForEntity(int entityNum) {
        if (not initialized or not IsValidEntity(entityNum)) {
            return;
        }

        if (entityLoops[entityNum].sound) {
            entityLoops[entityNum].addedThisFrame = false;
        }
    }

    void StartMusic(Str::StringRef leadingSound, Str::StringRef loopSound) {
        if (not initialized) {
            return;
        }

        StopMusic();
        music = std::make_shared<MusicSound>(leadingSound, loopSound);
        AddSound(GetLocalEmitter(), music, 1);
    }

    void StopMusic() {
        if (not initialized) {
            return;
        }

        if (music) {
            music->Stop();
        }
        music = nullptr;
    }

    void StreamData(int streamNum, const void* data, int numSamples, int rate, int width, float volume, int entityNum) {
        if (not initialized or (streamNum < 0 or streamNum >= N_STREAMS)) {
            return;
        }

        if (not streams[streamNum]) {
            streams[streamNum] = std::make_shared<StreamingSound>();
            if (IsValidEntity(entityNum)) {
                AddSound(GetLocalEmitter(), streams[streamNum], 1);
            } else {
                AddSound(GetEmitterForEntity(entityNum), streams[streamNum], 1);
            }
        }

        streams[streamNum]->SetGain(volume);

        snd_info_t dataInfo = {rate, width, 1, numSamples, (width * numSamples), 0};
        AL::Buffer buffer;

        int feedError = buffer.Feed(dataInfo, data);

        if (not feedError) {
            streams[streamNum]->AppendBuffer(std::move(buffer));
        }
    }

    void UpdateListener(int entityNum, const vec3_t orientation[3]) {
        if (not initialized or
            not IsValidEntity(entityNum) or
            not orientation or
            not IsValidVector(orientation[0]) or
            not IsValidVector(orientation[1]) or
            not IsValidVector(orientation[2])) {
            return;
        }

        UpdateListenerEntity(entityNum, orientation);
    }

    void UpdateEntityPosition(int entityNum, const vec3_t position) {
        if (not initialized or not IsValidEntity(entityNum) or not IsValidVector(position)) {
            return;
        }

        UpdateRegisteredEntityPosition(entityNum, position);
    }

    void UpdateEntityVelocity(int entityNum, const vec3_t velocity) {
        if (not initialized or not IsValidEntity(entityNum) or not IsValidVector(velocity)) {
            return;
        }

        UpdateRegisteredEntityVelocity(entityNum, velocity);
    }

    void UpdateEntityOcclusion(int entityNum, float ratio) {
        if (not initialized or not IsValidEntity(entityNum) or std::isnan(ratio)) {
            return;
        }

        UpdateRegisteredEntityOcclusion(entityNum, ratio);
    }

    // Capture functions

    static AL::CaptureDevice* capture = nullptr;

    void StartCapture(int rate) {
        if (capture or not initialized) {
            return;
        }

        capture = AL::CaptureDevice::GetDefaultDevice(rate);
        capture->Start();
    }

    int AvailableCaptureSamples() {
        if (not capture) {
            return 0;
        }

        return capture->GetNumCapturedSamples();
    }

    void GetCapturedData(int numSamples, void* buffer) {
        if (not capture) {
            return;
        }

        capture->Capture(numSamples, buffer);
    }

    void StopCapture() {
        if (not initialized) {
            return;
        }

        if (capture) {
            capture->Stop();
        }
        delete capture;
        capture = nullptr;
    }

    // Console commands

    class ALInfoCmd : public Cmd::StaticCmd {
        public:
            ALInfoCmd(): StaticCmd("printALInfo", Cmd::AUDIO, N_("Prints information about OpenAL")) {
            }

            virtual void Run(const Cmd::Args&) const OVERRIDE {
                Print(AL::GetSystemInfo(device, capture));
            }
    };
    static ALInfoCmd alInfoRegistration;

    class ListSamplesCmd : public Cmd::StaticCmd {
        public:
            ListSamplesCmd(): StaticCmd("listAudioSamples", Cmd::AUDIO, N_("Lists all the loaded sound samples")) {
            }

            virtual void Run(const Cmd::Args&) const OVERRIDE {
                std::vector<std::string> samples = ListSamples();

                std::sort(samples.begin(), samples.end());

                for (auto& sample: samples) {
                    Print(sample);
                }
                Print("%i samples", samples.size());
            }
    };
    static ListSamplesCmd listSamplesRegistration;

    class StopSoundsCmd : public Cmd::StaticCmd {
        public:
            StopSoundsCmd(): StaticCmd("stopSounds", Cmd::AUDIO, N_("Stops the music and the looping sounds")) {
            }

            virtual void Run(const Cmd::Args&) const OVERRIDE {
                ClearAllLoopingSounds();
                StopMusic();
            }
    };
    static StopSoundsCmd stopSoundsRegistration;

}
<|endoftext|>


<commit_before>/*##############################################################################

    Copyright (C) 2011 HPCC Systems.

    All rights reserved. This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
############################################################################## */

#include "build-config.h"
#include "platform.h"
#include "thirdparty.h"
#include "portlist.h"
#include "jlib.hpp"
#include "jlog.ipp"
#include "jptree.hpp"
#include "jmisc.hpp"

#include "mpbase.hpp"
#include "mpcomm.hpp"
#include "mplog.hpp"
#include "dasess.hpp"
#include "dasds.hpp"
#include "daclient.hpp"
#include "dalienv.hpp"
#include "dllserver.hpp"
#include "rmtfile.hpp"

#include "saserver.hpp"
#include "saarch.hpp"
#include "saverify.hpp"
#include "saxref.hpp"
#include "saqmon.hpp"
#include "sacoalescer.hpp"
#include "sacmd.hpp"

extern void LDStest();

#define SASHAVERSION "1.4"

#define DEFAULT_PERF_REPORT_DELAY (60*5)

Owned<IPropertyTree> serverConfig;
static IArrayOf<ISashaServer> servers;
static ILogMsgHandler * fileMsgHandler=NULL;
static CSDSServerStatus * SashaServerStatus=NULL;
static atomic_t StopSuspendCount = ATOMIC_INIT(0);
static bool stopped = false;
static Semaphore stopSem;

const char *sashaProgramName;

CSuspendAutoStop::CSuspendAutoStop() { atomic_inc(&StopSuspendCount); }
CSuspendAutoStop::~CSuspendAutoStop() { atomic_dec(&StopSuspendCount); }

void setMsgLevel(unsigned level)
{
    if (!fileMsgHandler)
        return;
    ILogMsgFilter *filter = getSwitchLogMsgFilterOwn(getComponentLogMsgFilter(3), getCategoryLogMsgFilter(MSGAUD_all, MSGCLS_all, level, true), getDefaultLogMsgFilter());
    queryLogMsgManager()->changeMonitorFilter(queryStderrLogMsgHandler(), filter);
    queryLogMsgManager()->changeMonitorFilterOwn(fileMsgHandler, filter);
}

static void AddServers()
{
    // order significant
    servers.append(*createSashaArchiverServer());
//  servers.append(*createSashaVerifierServer());
    servers.append(*createSashaSDSCoalescingServer());
    servers.append(*createSashaXrefServer());
    servers.append(*createSashaDaFSMonitorServer());
    servers.append(*createSashaQMonitorServer());
    servers.append(*createSashaFileExpiryServer());
    // add new servers here
}

static void stopServer()
{
    ForEachItemInRev(i,servers)
    {
        ISashaServer &server=servers.item(i);
        LOG(MCprogress, unknownJob, "Stopping %d",i);
        server.stop();
    }
    ForEachItemInRev(j,servers)
    {
        servers.remove(j);      // ensure correct order for destruction
    }
}

static bool actionOnAbort()
{
    LOG(MCprogress, unknownJob, "Stop signalled");
    if (stopped)
    {
        LOG(MCprogress, unknownJob, "Previously marked stopped. Killing process..");
        queryLogMsgManager()->flushQueue(10*1000);
#ifdef _WIN32
        TerminateProcess(GetCurrentProcess(), 1);
#else
        kill(getpid(), SIGTERM);
#endif
    }
    else
    {
        requestStop(NULL);
        return false;
    }
    return true;
}

void requestStop(IException *e)
{
    if (e)
        LOG(MCoperatorError, unknownJob, e, "SASERVER: Unexpected exception, saserver terminating");
    LOG(MCprogress, unknownJob, "Stop requested");
    stopSem.signal();
}

void stopSashaServer(const char *eps,unsigned short port)
{
    SocketEndpoint ep;
    if (eps&&*eps)
        ep.set(eps,port);
    else
        ep.setLocalHost(port);

    Owned<INode> node = createINode(ep);
    IInterCommunicator & comm=queryWorldCommunicator();
    Owned<ISashaCommand> cmd = createSashaCommand();
    cmd->setAction(SCA_STOP);
    if (!queryWorldCommunicator().verifyConnection(node,1000)) {
        PROGLOG("Sasha Server already stopped");
        return;
    }
    PROGLOG("Sasha Server being stopped");
    cmd->send(node,1000*60);
    while (queryWorldCommunicator().verifyConnection(node,500)) {
        PROGLOG("Waiting for Sasha Server to stop....");
        Sleep(2000);
    }
}

void usage()
{
    printf("Usage: SASERVER     -- (all configuration parameters in sashaconf.xml)\n"
           "or:    SASERVER STOP\n");
}

class CSashaCmdThread : public CInterface, implements IPooledThread
{
    Owned<ISashaCommand> cmd;
public:
    IMPLEMENT_IINTERFACE;
    void init(void *param)
    {
        cmd.setown((ISashaCommand *)param);
    }
    void main()
    {
        StringAttrArray out;
        StringAttrArray outres;
        if (cmd->getAction()==SCA_STOP) {
            if (!stopped) {
                stopped = true;
                cmd->cancelaccept();
                PROGLOG("Request to stop received");
            }
        }
        else if (cmd->getAction()==SCA_GETVERSION)
            cmd->addId(SASHAVERSION); // should be a result probably but keep bwd compatible
        else if (cmd->getAction()==SCA_COALESCE_SUSPEND)
            suspendCoalescingServer();
        else if (cmd->getAction()==SCA_COALESCE_RESUME)
            resumeCoalescingServer();
        else if (cmd->getAction()==SCA_XREF)
            processXRefRequest(cmd);
        else if (!processArchiverCommand(cmd))
            WARNLOG("Command %d not handled",cmd->getAction());
        if (cmd->getAction()==SCA_WORKUNIT_SERVICES_GET)
            cmd->WUSreply();
        else
            cmd->reply();
    }
    bool stop()
    {
        return true;
    }
    bool canReuse()
    {
        return false;
    }
};

void SashaMain()
{
    IInterCommunicator & comm=queryWorldCommunicator();
    unsigned start = msTick();
    unsigned timeout = serverConfig->getPropInt("@autoRestartInterval")*1000*60*60;
    class cCmdPoolFactory : public CInterface, implements IThreadFactory
    {
    public:
        IMPLEMENT_IINTERFACE;
        IPooledThread *createNew()
        {
            return new CSashaCmdThread;
        }
    } factory;
    Owned<IThreadPool> threadpool = createThreadPool("sachaCmdPool",&factory);
    CMessageBuffer mb;
    while (!stopped) {
        try {
            Owned<ISashaCommand> cmd = createSashaCommand();
            if (cmd->accept(5*60*1000)) {
                threadpool->start(cmd.getClear());
            }
            else if (stopped) {
                PROGLOG("Sasha stopping");
                break;
            }
            else if (!verifyCovenConnection(5*60*1000)) {
                PROGLOG("Dali stopped");
                stopped = true;
            }
            else if (timeout&&(timeout<msTick()-start)) {
                if (atomic_read(&StopSuspendCount)==0) {
                    PROGLOG("Auto Restart");
                    stopped = true;
                }
                else
                    PROGLOG("Waiting to Auto Restart %d",msTick()-start-timeout);
            }

        }
        catch (IException *e)
        {
            EXCLOG(e, "SashaMain");
            e->Release();
        }
    }
    if (!threadpool->joinAll(true,1000*60))
        PROGLOG("Stopping join timed out");
}

static struct CRequestStop : implements IExceptionHandler
{
    virtual bool fireException(IException *e)
    {
        requestStop(e);
        return true;
    }
} exceptionStopHandler;

int main(int argc, const char* argv[])
{
    InitModuleObjects();
    EnableSEHtoExceptionMapping();

    sashaProgramName = argv[0];

#ifndef __64BIT__
    Thread::setDefaultStackSize(0x20000);
#endif
    setDaliServixSocketCaching(true);
    bool stop = false;
    bool coalescer = false;
    bool force = false;
    if (argc>1) {
        if (stricmp(argv[1],"STOP")==0)
            stop = true;
        else if (stricmp(argv[1],"COALESCE")==0) {
            coalescer = true;
            force = (argc>2)&&(stricmp(argv[2],"FORCE")==0);
        }
        else {
            usage();
            return 1;
        }
    }
#ifndef _DEBUG
    NoQuickEditSection x;
#endif

    Owned<IFile> sentinelFile;
    if (!coalescer)
    {
        sentinelFile.setown(createSentinelTarget());
        removeSentinelFile(sentinelFile);
    }

    OwnedIFile ifile = createIFile("sashaconf.xml");
    serverConfig.setown(ifile->exists()?createPTreeFromXMLFile("sashaconf.xml"):createPTree());
    StringBuffer daliServer;
    if (!serverConfig->getProp("@DALISERVERS", daliServer)||(daliServer.length()==0)) {
        printf("DALISERVERS not specified in sashaconf.xml");
        return 1;
    }

    StringBuffer logname;
    StringBuffer logdir;
    if (!getConfigurationDirectory(serverConfig->queryPropTree("Directories"),"log","sasha",serverConfig->queryProp("@name"),logdir))
        serverConfig->getProp("@logDir", logdir);
    if (logdir.length() && recursiveCreateDirectory(logdir.str()))
        logname.append(logdir);
    else
    {
        char cwd[1024];
        GetCurrentDirectory(1024, cwd);
        logname.append(cwd);
    }
    addPathSepChar(logname);

    if (coalescer) {
        logname.append("saserver.coalesce");
        addFileTimestamp(logname, true);
        logname.append(".log");
        appendLogFile(logname.str(),0,false);
    }
    else if (!stop) {
        logname.append("saserver");
        StringBuffer aliaslogname(logname);
        aliaslogname.append(".log");
        fileMsgHandler = getRollingFileLogMsgHandler(logname.str(), ".log", MSGFIELD_STANDARD, false, true, NULL, aliaslogname.str());
        queryLogMsgManager()->addMonitorOwn(fileMsgHandler, getCategoryLogMsgFilter(MSGAUD_all, MSGCLS_all, TopDetail));
        DBGLOG("Build %s", BUILD_TAG);
    }

    bool enableSNMP = false;

    Owned<IPerfMonHook> perfMonHook;
    try {

        unsigned short port = (stop||coalescer)?0:DEFAULT_SASHA_PORT;
        Owned<IGroup> serverGroup = createIGroup(daliServer.str(),DALI_SERVER_PORT);
        initClientProcess(serverGroup, DCR_SashaServer, port, NULL, NULL, MP_WAIT_FOREVER);
        setPasswordsFromSDS();
        if (!stop&!coalescer) {
            startLogMsgParentReceiver();    // for auditing
            connectLogMsgManagerToDali();
        }

        if (stop) {
            stopSashaServer((argc>2)?argv[2]:"",DEFAULT_SASHA_PORT);
        }
        else {
            if (serverConfig->getPropInt("@enableSysLog"),true)
                UseSysLogForOperatorMessages();
            if (coalescer) {
                coalesceDatastore(force);
            }
            else {
                startPerformanceMonitor(serverConfig->getPropInt("@perfReportDelay", DEFAULT_PERF_REPORT_DELAY)*1000, PerfMonStandard, perfMonHook);
                AddServers();
                addAbortHandler(actionOnAbort);

                StringBuffer eps;
                PROGLOG("SASERVER starting on %s",queryMyNode()->endpoint().getUrlStr(eps).str());

                ForEachItemIn(i1,servers)
                {
                    ISashaServer &server=servers.item(i1);
                    server.start();
                }

                SashaServerStatus = new CSDSServerStatus("SashaServer");

                ForEachItemIn(i2,servers)
                {
                    ISashaServer &server=servers.item(i2);
                    server.ready();
                }
                class CStopThread : implements IThreaded
                {
                    CThreaded threaded;
                public:
                    CStopThread() : threaded("CStopThread") { threaded.init(this); }
                    ~CStopThread() { threaded.join(); }
                    virtual void main()
                    {
                        stopSem.wait();
                        if (!stopped)
                        {
                            stopped = true;
                            IInterCommunicator &comm=queryWorldCommunicator();
                            comm.cancel(NULL, MPTAG_SASHA_REQUEST);
                        }
                    }
                } *stopThread = new CStopThread;
                addThreadExceptionHandler(&exceptionStopHandler);
                writeSentinelFile(sentinelFile);
                SashaMain();
                removeThreadExceptionHandler(&exceptionStopHandler);

                stopSem.signal();
                delete stopThread;

                PROGLOG("SASERVER exiting");
                stopPerformanceMonitor();
            }
            delete SashaServerStatus;
            SashaServerStatus = NULL;
        }
    }
    catch(IException *e){
        EXCLOG(e, "Sasha Server Exception: ");
        stopPerformanceMonitor();
        e->Release();
    }
    catch (const char *s) {
        WARNLOG("Sasha: %s",s);
    }

    if (!stop)
    {
        stopServer();
    }
    else if (stop)
        Sleep(2000);    // give time to stop
    serverConfig.clear();
    try {
        closeDllServer();
        closeEnvironment();
        closedownClientProcess();
    }
    catch (IException *) {  // dali may be down
    }
    UseSysLogForOperatorMessages(false);
    releaseAtoms();
    return 0;
}

<commit_msg>Fix gh-683 - Reduce length verify timeout<commit_after>/*##############################################################################

    Copyright (C) 2011 HPCC Systems.

    All rights reserved. This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
############################################################################## */

#include "build-config.h"
#include "platform.h"
#include "thirdparty.h"
#include "portlist.h"
#include "jlib.hpp"
#include "jlog.ipp"
#include "jptree.hpp"
#include "jmisc.hpp"

#include "mpbase.hpp"
#include "mpcomm.hpp"
#include "mplog.hpp"
#include "dasess.hpp"
#include "dasds.hpp"
#include "daclient.hpp"
#include "dalienv.hpp"
#include "dllserver.hpp"
#include "rmtfile.hpp"

#include "saserver.hpp"
#include "saarch.hpp"
#include "saverify.hpp"
#include "saxref.hpp"
#include "saqmon.hpp"
#include "sacoalescer.hpp"
#include "sacmd.hpp"

extern void LDStest();

#define SASHAVERSION "1.4"

#define DEFAULT_PERF_REPORT_DELAY (60*5)

Owned<IPropertyTree> serverConfig;
static IArrayOf<ISashaServer> servers;
static ILogMsgHandler * fileMsgHandler=NULL;
static CSDSServerStatus * SashaServerStatus=NULL;
static atomic_t StopSuspendCount = ATOMIC_INIT(0);
static bool stopped = false;
static Semaphore stopSem;

const char *sashaProgramName;

CSuspendAutoStop::CSuspendAutoStop() { atomic_inc(&StopSuspendCount); }
CSuspendAutoStop::~CSuspendAutoStop() { atomic_dec(&StopSuspendCount); }

void setMsgLevel(unsigned level)
{
    if (!fileMsgHandler)
        return;
    ILogMsgFilter *filter = getSwitchLogMsgFilterOwn(getComponentLogMsgFilter(3), getCategoryLogMsgFilter(MSGAUD_all, MSGCLS_all, level, true), getDefaultLogMsgFilter());
    queryLogMsgManager()->changeMonitorFilter(queryStderrLogMsgHandler(), filter);
    queryLogMsgManager()->changeMonitorFilterOwn(fileMsgHandler, filter);
}

static void AddServers()
{
    // order significant
    servers.append(*createSashaArchiverServer());
//  servers.append(*createSashaVerifierServer());
    servers.append(*createSashaSDSCoalescingServer());
    servers.append(*createSashaXrefServer());
    servers.append(*createSashaDaFSMonitorServer());
    servers.append(*createSashaQMonitorServer());
    servers.append(*createSashaFileExpiryServer());
    // add new servers here
}

static void stopServer()
{
    ForEachItemInRev(i,servers)
    {
        ISashaServer &server=servers.item(i);
        LOG(MCprogress, unknownJob, "Stopping %d",i);
        server.stop();
    }
    ForEachItemInRev(j,servers)
    {
        servers.remove(j);      // ensure correct order for destruction
    }
}

static bool actionOnAbort()
{
    LOG(MCprogress, unknownJob, "Stop signalled");
    if (stopped)
    {
        LOG(MCprogress, unknownJob, "Previously marked stopped. Killing process..");
        queryLogMsgManager()->flushQueue(10*1000);
#ifdef _WIN32
        TerminateProcess(GetCurrentProcess(), 1);
#else
        kill(getpid(), SIGTERM);
#endif
    }
    else
    {
        requestStop(NULL);
        return false;
    }
    return true;
}

void requestStop(IException *e)
{
    if (e)
        LOG(MCoperatorError, unknownJob, e, "SASERVER: Unexpected exception, saserver terminating");
    LOG(MCprogress, unknownJob, "Stop requested");
    stopSem.signal();
}

void stopSashaServer(const char *eps,unsigned short port)
{
    SocketEndpoint ep;
    if (eps&&*eps)
        ep.set(eps,port);
    else
        ep.setLocalHost(port);

    Owned<INode> node = createINode(ep);
    IInterCommunicator & comm=queryWorldCommunicator();
    Owned<ISashaCommand> cmd = createSashaCommand();
    cmd->setAction(SCA_STOP);
    if (!queryWorldCommunicator().verifyConnection(node,1000)) {
        PROGLOG("Sasha Server already stopped");
        return;
    }
    PROGLOG("Sasha Server being stopped");
    cmd->send(node,1000*60);
    while (queryWorldCommunicator().verifyConnection(node,500)) {
        PROGLOG("Waiting for Sasha Server to stop....");
        Sleep(2000);
    }
}

void usage()
{
    printf("Usage: SASERVER     -- (all configuration parameters in sashaconf.xml)\n"
           "or:    SASERVER STOP\n");
}

class CSashaCmdThread : public CInterface, implements IPooledThread
{
    Owned<ISashaCommand> cmd;
public:
    IMPLEMENT_IINTERFACE;
    void init(void *param)
    {
        cmd.setown((ISashaCommand *)param);
    }
    void main()
    {
        StringAttrArray out;
        StringAttrArray outres;
        if (cmd->getAction()==SCA_STOP) {
            if (!stopped) {
                stopped = true;
                cmd->cancelaccept();
                PROGLOG("Request to stop received");
            }
        }
        else if (cmd->getAction()==SCA_GETVERSION)
            cmd->addId(SASHAVERSION); // should be a result probably but keep bwd compatible
        else if (cmd->getAction()==SCA_COALESCE_SUSPEND)
            suspendCoalescingServer();
        else if (cmd->getAction()==SCA_COALESCE_RESUME)
            resumeCoalescingServer();
        else if (cmd->getAction()==SCA_XREF)
            processXRefRequest(cmd);
        else if (!processArchiverCommand(cmd))
            WARNLOG("Command %d not handled",cmd->getAction());
        if (cmd->getAction()==SCA_WORKUNIT_SERVICES_GET)
            cmd->WUSreply();
        else
            cmd->reply();
    }
    bool stop()
    {
        return true;
    }
    bool canReuse()
    {
        return false;
    }
};

void SashaMain()
{
    IInterCommunicator & comm=queryWorldCommunicator();
    unsigned start = msTick();
    unsigned timeout = serverConfig->getPropInt("@autoRestartInterval")*1000*60*60;
    class cCmdPoolFactory : public CInterface, implements IThreadFactory
    {
    public:
        IMPLEMENT_IINTERFACE;
        IPooledThread *createNew()
        {
            return new CSashaCmdThread;
        }
    } factory;
    Owned<IThreadPool> threadpool = createThreadPool("sachaCmdPool",&factory);
    CMessageBuffer mb;
    while (!stopped) {
        try {
            Owned<ISashaCommand> cmd = createSashaCommand();
            if (cmd->accept(5*60*1000)) {
                threadpool->start(cmd.getClear());
            }
            else if (stopped) {
                PROGLOG("Sasha stopping");
                break;
            }
            else if (!verifyCovenConnection(30*1000)) {
                PROGLOG("Dali stopped");
                stopped = true;
            }
            else if (timeout&&(timeout<msTick()-start)) {
                if (atomic_read(&StopSuspendCount)==0) {
                    PROGLOG("Auto Restart");
                    stopped = true;
                }
                else
                    PROGLOG("Waiting to Auto Restart %d",msTick()-start-timeout);
            }

        }
        catch (IException *e)
        {
            EXCLOG(e, "SashaMain");
            e->Release();
        }
    }
    if (!threadpool->joinAll(true,1000*60))
        PROGLOG("Stopping join timed out");
}

static struct CRequestStop : implements IExceptionHandler
{
    virtual bool fireException(IException *e)
    {
        requestStop(e);
        return true;
    }
} exceptionStopHandler;

int main(int argc, const char* argv[])
{
    InitModuleObjects();
    EnableSEHtoExceptionMapping();

    sashaProgramName = argv[0];

#ifndef __64BIT__
    Thread::setDefaultStackSize(0x20000);
#endif
    setDaliServixSocketCaching(true);
    bool stop = false;
    bool coalescer = false;
    bool force = false;
    if (argc>1) {
        if (stricmp(argv[1],"STOP")==0)
            stop = true;
        else if (stricmp(argv[1],"COALESCE")==0) {
            coalescer = true;
            force = (argc>2)&&(stricmp(argv[2],"FORCE")==0);
        }
        else {
            usage();
            return 1;
        }
    }
#ifndef _DEBUG
    NoQuickEditSection x;
#endif

    Owned<IFile> sentinelFile;
    if (!coalescer)
    {
        sentinelFile.setown(createSentinelTarget());
        removeSentinelFile(sentinelFile);
    }

    OwnedIFile ifile = createIFile("sashaconf.xml");
    serverConfig.setown(ifile->exists()?createPTreeFromXMLFile("sashaconf.xml"):createPTree());
    StringBuffer daliServer;
    if (!serverConfig->getProp("@DALISERVERS", daliServer)||(daliServer.length()==0)) {
        printf("DALISERVERS not specified in sashaconf.xml");
        return 1;
    }

    StringBuffer logname;
    StringBuffer logdir;
    if (!getConfigurationDirectory(serverConfig->queryPropTree("Directories"),"log","sasha",serverConfig->queryProp("@name"),logdir))
        serverConfig->getProp("@logDir", logdir);
    if (logdir.length() && recursiveCreateDirectory(logdir.str()))
        logname.append(logdir);
    else
    {
        char cwd[1024];
        GetCurrentDirectory(1024, cwd);
        logname.append(cwd);
    }
    addPathSepChar(logname);

    if (coalescer) {
        logname.append("saserver.coalesce");
        addFileTimestamp(logname, true);
        logname.append(".log");
        appendLogFile(logname.str(),0,false);
    }
    else if (!stop) {
        logname.append("saserver");
        StringBuffer aliaslogname(logname);
        aliaslogname.append(".log");
        fileMsgHandler = getRollingFileLogMsgHandler(logname.str(), ".log", MSGFIELD_STANDARD, false, true, NULL, aliaslogname.str());
        queryLogMsgManager()->addMonitorOwn(fileMsgHandler, getCategoryLogMsgFilter(MSGAUD_all, MSGCLS_all, TopDetail));
        DBGLOG("Build %s", BUILD_TAG);
    }

    bool enableSNMP = false;

    Owned<IPerfMonHook> perfMonHook;
    try {

        unsigned short port = (stop||coalescer)?0:DEFAULT_SASHA_PORT;
        Owned<IGroup> serverGroup = createIGroup(daliServer.str(),DALI_SERVER_PORT);
        initClientProcess(serverGroup, DCR_SashaServer, port, NULL, NULL, MP_WAIT_FOREVER);
        setPasswordsFromSDS();
        if (!stop&!coalescer) {
            startLogMsgParentReceiver();    // for auditing
            connectLogMsgManagerToDali();
        }

        if (stop) {
            stopSashaServer((argc>2)?argv[2]:"",DEFAULT_SASHA_PORT);
        }
        else {
            if (serverConfig->getPropInt("@enableSysLog"),true)
                UseSysLogForOperatorMessages();
            if (coalescer) {
                coalesceDatastore(force);
            }
            else {
                startPerformanceMonitor(serverConfig->getPropInt("@perfReportDelay", DEFAULT_PERF_REPORT_DELAY)*1000, PerfMonStandard, perfMonHook);
                AddServers();
                addAbortHandler(actionOnAbort);

                StringBuffer eps;
                PROGLOG("SASERVER starting on %s",queryMyNode()->endpoint().getUrlStr(eps).str());

                ForEachItemIn(i1,servers)
                {
                    ISashaServer &server=servers.item(i1);
                    server.start();
                }

                SashaServerStatus = new CSDSServerStatus("SashaServer");

                ForEachItemIn(i2,servers)
                {
                    ISashaServer &server=servers.item(i2);
                    server.ready();
                }
                class CStopThread : implements IThreaded
                {
                    CThreaded threaded;
                public:
                    CStopThread() : threaded("CStopThread") { threaded.init(this); }
                    ~CStopThread() { threaded.join(); }
                    virtual void main()
                    {
                        stopSem.wait();
                        if (!stopped)
                        {
                            stopped = true;
                            IInterCommunicator &comm=queryWorldCommunicator();
                            comm.cancel(NULL, MPTAG_SASHA_REQUEST);
                        }
                    }
                } *stopThread = new CStopThread;
                addThreadExceptionHandler(&exceptionStopHandler);
                writeSentinelFile(sentinelFile);
                SashaMain();
                removeThreadExceptionHandler(&exceptionStopHandler);

                stopSem.signal();
                delete stopThread;

                PROGLOG("SASERVER exiting");
                stopPerformanceMonitor();
            }
            delete SashaServerStatus;
            SashaServerStatus = NULL;
        }
    }
    catch(IException *e){
        EXCLOG(e, "Sasha Server Exception: ");
        stopPerformanceMonitor();
        e->Release();
    }
    catch (const char *s) {
        WARNLOG("Sasha: %s",s);
    }

    if (!stop)
    {
        stopServer();
    }
    else if (stop)
        Sleep(2000);    // give time to stop
    serverConfig.clear();
    try {
        closeDllServer();
        closeEnvironment();
        closedownClientProcess();
    }
    catch (IException *) {  // dali may be down
    }
    UseSysLogForOperatorMessages(false);
    releaseAtoms();
    return 0;
}

<|endoftext|>


<commit_before>// Copyright (c) 2020 by Robert Bosch GmbH. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#pragma once

#include <sys/time.h>

using timer_t = void*;

struct itimerspec
{
    timespec it_interval;
    timespec it_value;
};

#if 0
#include <dispatch/dispatch.h>

#include <chrono>
#include <iostream>
#include <thread>

int test(dispatch_source_t sourceTimer) { printf("test called\n"); }

int main() {
    dispatch_queue_attr_t queueAttributes = 0;
    printf("creating queue\n");
    dispatch_queue_t timerQueue =
        dispatch_queue_create("timerQueueName", queueAttributes);

    uintptr_t timerHandle;
    unsigned long timerMask;
    printf("creating timer\n");
    dispatch_source_t timer = dispatch_source_create(
        DISPATCH_SOURCE_TYPE_TIMER, timerHandle, timerMask, timerQueue);

    printf("set handler\n");
    dispatch_source_set_event_handler(timer, ^{
      test(timer);
    });

    printf("set cancel handler\n");
    dispatch_source_set_cancel_handler(timer, ^{
      dispatch_release(timer);
      dispatch_release(timerQueue);
      printf("cleaned up timer and timerQueue");
    });

    printf("set timer interval\n");
    dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC / 10);
    dispatch_source_set_timer(timer, start, 1 * NSEC_PER_SEC / 10, 0);

    printf("activate timer\n");
    dispatch_resume(timer);

    std::this_thread::sleep_for(std::chrono::seconds(1));

    printf("cleaning up timer\n");
    dispatch_source_cancel(timer);
}

#endif

inline int timer_create(clockid_t clockid, struct sigevent* sevp, timer_t* timerid)
{
    // dispatch_queue_t timerQueue =
    //    dispatch_queue_create("timerQueueName", queueAttributes);
    // dispatch_source_t timer = dispatch_source_create(
    //    DISPATCH_SOURCE_TYPE_TIMER, timerHandle, timerMask, timerQueue);
    // dispatch_source_set_cancel_handler(timer, ^{
    //  dispatch_release(timer);
    //  dispatch_release(timerQueue);
    //  printf("cleaned up timer and timerQueue");
    //});
    // dispatch_resume(timer);
    return 0;
}

inline int timer_delete(timer_t timerid)
{
    // dispatch_source_cancel(timer);
    return 0;
}

inline int timer_settime(timer_t timerid, int flags, const struct itimerspec* new_value, struct itimerspec* old_value)
{
    // dispatch_source_set_timer(timer, start, 1 * NSEC_PER_SEC / 10, 0);
    return 0;
}

inline int timer_gettime(timer_t timerid, struct itimerspec* curr_value)
{
    return 0;
}

inline int timer_getoverrun(timer_t timerid)
{
    return 0;
}
<commit_msg>iox-#32 working in timer<commit_after>// Copyright (c) 2020 by Robert Bosch GmbH. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#pragma once

#include <dispatch/dispatch.h>
#include <sys/time.h>

struct itimerspec
{
    timespec it_interval;
    timespec it_value;
};

struct appleTimer_t
{
    dispatch_source_t timer;
};

using timer_t = appleTimer_t*;

int timer_create(clockid_t clockid, struct sigevent* sevp, timer_t* timerid);
int timer_delete(timer_t timerid);
int timer_settime(timer_t timerid, int flags, const struct itimerspec* new_value, struct itimerspec* old_value);
int timer_gettime(timer_t timerid, struct itimerspec* curr_value);
int timer_getoverrun(timer_t timerid);
<|endoftext|>