/* FARGOS Development, LLC Sample Programs Copyright (C) 1999 - 2020 FARGOS Development, LLC. All rights reserved. mailto:support@fargos.net for assistance. */ #include #include #include #include #include #define THIS_COMPONENT app #ifdef _WIN32 /* NOTE: in gdbm library, file gdbmopen.c, all calls to open() function need _O_BINARY added to mask to prevent CR/LF translations */ #include #else /* Unix */ #include #include #include #include #include #endif /* end Unix variants */ #if USE_LMDB == 1 #include MDB_env *LMDB_env; MDB_dbi LMDB_database_id; #else /* use gdbm/ndbm */ #if _WIN32 #define GDBM_STATIC #include #define USE_GDBM_EQUIVALENTS #else /* Unix */ #ifndef USE_NATIVE_NDBM // Linux doesn't have a good ndbm.h, so use gdbm.h // Solaris ndbm doesn't handle large objects #include #define USE_GDBM_EQUIVALENTS #else /* USE_NATIVE_NDBM defined */ // probably *BSD #include static DBM *PDdbHandle; #endif /* USE_NATIVE_NDBM */ #endif /* WIN32/Unix */ #ifdef USE_GDBM_EQUIVALENTS static GDBM_FILE PDdbHandle; #define DBM_REPLACE GDBM_REPLACE #define dbm_open(fn, flags, mode) gdbm_open(fn, 0, GDBM_WRCREAT | GDBM_SYNC | GDBM_NOLOCK, mode, 0) #define dbm_close(d) gdbm_close(d) #define dbm_store(d, k, c, f) gdbm_store(d, k, c, f) #define dbm_fetch(d, k) gdbm_fetch(d, k) #define dbm_delete(d, k) gdbm_delete(d, k) #define dbm_firstkey(d) gdbm_firstkey(d) // "key" is taken from variable used in doListObjects() routine #define dbm_nextkey(d) gdbm_nextkey(d, key) #endif /* end USE_GDBM_EQUIVALENTS */ #endif /* LMDB */ typedef void (*initFP)(); initFP moduleList[1]; char *moduleFiles[1]; #ifdef _WIN32 extern "C" int flock(int fd, int modeBits) { // NOTE: never called, so a stub is good enough /* BIT FLAGS: one of: LOCK_SH (shared) LOCK_EX (exclusive) LOCK_UN (unlock) modified by: LOCK_NB (nonblocking) */ // Closest Win32-equivalent: LockFileEx(), UnlockFileEx() // flock returns 0 ==> success return (0); } extern "C" int fsync(int fd) { // FlushFileBuffers((HANDLE) fd); // Win32 _commit(fd); return (0); } #endif #if sun extern "C" int flock(int fd, int modeBits) { // NOTE: never called, so a stub is good enough // mode: F_LOCK, F_ULOCK // lockf(fd, mode, 0); return (0); } #endif //void OMEthread::terminateThread() {} static const char srcID[] OME_USED = "$Id: persistd.cpp 326 2020-03-27 01:48:37Z geoff $"; #define OBJECT_PAGER_SERVICE "/ObjectPager" #define REGISTERED_AS "/PersistentObjectDatabase" static OMEapi *api; static OMEtype connectionUserInfo; static OMEtype replyMethodName; static char addr[256]; static const char *dbFileName; static char pagerServiceName[256]; static char thisServiceName[256]; // flag settings from clPersistentObj.oil #define ALWAYS_RESTORE 0 #define RESTORE_ON_DEMAND 1 #define TEMPORARY 2 #ifdef _WIN32 #define SUPPORT_SYSTEM_TRAY #define VISTA_DAEMON_NAME "OMEpersistd" #include #endif #ifdef _WIN32 static int initWinsock() { WSADATA data; WORD verRequested = MAKEWORD(2, 0); int rc = WSAStartup(verRequested, &data); return (rc); } #endif #if USE_LMDB == 1 int save_object(const OMEstring &key, const OMEstring &blob) { MDB_txn *txn; MDB_dbi dbi; MDB_val keyData, objData; int rc = mdb_txn_begin(LMDB_env, nullptr, 0, &txn); if (rc != 0) { LOG_CERR(error) << "Could not create transaction, rc=" << rc << " errMess=\"" << mdb_strerror(rc) << "\"" << LOG_ENDLINE; return (-1); } rc = mdb_dbi_open(txn, nullptr, MDB_CREATE, &dbi); if (rc != 0) { LOG_CERR(error) << "database cannot be opened for save_object," << " errMess=\"" << mdb_strerror(rc) << "\"" << LOG_ENDLINE; mdb_txn_abort(txn); return (-1); } LOG_CERR(trace) << "save keyLen=" << key.length() << " blobLen=" << blob.length() << " key=\"" << AS_HEXADECIMAL_BUFFER(static_cast(key), key.length()) << "\"" << LOG_ENDLINE; keyData.mv_data = const_cast(static_cast(key)); keyData.mv_size = key.length(); objData.mv_data = const_cast(static_cast(blob)); objData.mv_size = blob.length(); rc = mdb_put(txn, dbi, &keyData, &objData, 0); if (rc != 0) { LOG_CERR(error) << "Could not put object," << " errMess=\"" << mdb_strerror(rc) << "\"" << LOG_ENDLINE; mdb_txn_abort(txn); return (-1); } // mdb_dbi_close(LMDB_env, dbi) is intentionally not called rc = mdb_txn_commit(txn); if (rc != 0) { LOG_CERR(error) << "Could not commit transaction," << " errMess=\"" << mdb_strerror(rc) << "\"" << LOG_ENDLINE; return (-1); } return (0); } #else /* dbm */ int save_object(const OMEstring &key, const OMEstring &blob) { datum keyData, objData; keyData.dptr = const_cast(static_cast(key)); keyData.dsize = key.length(); objData.dptr = const_cast(static_cast(blob)); objData.dsize = blob.length(); int rc = dbm_store(PDdbHandle, keyData, objData, DBM_REPLACE); #ifdef USE_GDBM_EQUIVALENTS gdbm_sync(PDdbHandle); #else fsync(dbm_dirfno(PDdbHandle)); fsync(dbm_pagfno(PDdbHandle)); #endif return (rc); } #endif #if USE_LMDB == 1 int remove_object(const OMEstring &key) { MDB_txn *txn; MDB_dbi dbi; MDB_val keyData; int rc = mdb_txn_begin(LMDB_env, nullptr, 0, &txn); if (rc != 0) { LOG_CERR(error) << "Could not create transaction," << " errMess=\"" << mdb_strerror(rc) << "\"" << LOG_ENDLINE; return (-1); } rc = mdb_dbi_open(txn, nullptr, 0, &dbi); if (rc != 0) { LOG_CERR(error) << "database does not exist to remove_object," << " errMess=\"" << mdb_strerror(rc) << "\"" << LOG_ENDLINE; mdb_txn_abort(txn); return (-1); } keyData.mv_data = const_cast(static_cast(key)); keyData.mv_size = key.length(); rc = mdb_del(txn, dbi, &keyData, nullptr); if (rc != 0) { LOG_CERR(error) << "Could not delete object," << " errMess=\"" << mdb_strerror(rc) << "\"" << LOG_ENDLINE; mdb_txn_abort(txn); return (-1); } // mdb_dbi_close(LMDB_env, dbi) is intentionally not called rc = mdb_txn_commit(txn); if (rc != 0) { LOG_CERR(error) << "Could not commit transaction," << " errMess=\"" << mdb_strerror(rc) << "\"" << LOG_ENDLINE; return (-1); } return (0); } #else /* dbm */ int remove_object(const OMEstring &key) { datum keyData; keyData.dptr = const_cast(static_cast(key)); keyData.dsize = key, length(); int rc = dbm_delete(PDdbHandle, keyData); #ifdef USE_GDBM_EQUIVALENTS gdbm_sync(PDdbHandle); #endif } #endif #if USE_LMDB == 1 OMEstring *read_object(const OMEstring &key) { MDB_txn *txn; MDB_dbi dbi; MDB_val keyData, objData; int rc = mdb_txn_begin(LMDB_env, nullptr, MDB_RDONLY, &txn); if (rc != 0) { LOG_CERR(error) << "Could not create transaction," << " errMess=\"" << mdb_strerror(rc) << "\"" << LOG_ENDLINE; return (nullptr); } rc = mdb_dbi_open(txn, nullptr, 0, &dbi); if (rc != 0) { LOG_CERR(error) << "database does not exist to read_object," << " errMess=\"" << mdb_strerror(rc) << "\"" << LOG_ENDLINE; mdb_txn_abort(txn); return (nullptr); } LOG_CERR(trace) << "read key len=" << key.length() << " key=\"" << AS_HEXADECIMAL_BUFFER(static_cast(key), key.length()) << "\"" << LOG_ENDLINE; keyData.mv_data = const_cast(static_cast(key)); keyData.mv_size = key.length(); LOG_CERR(trace) << "request keyData=\"" << AS_HEXADECIMAL_BUFFER(keyData.mv_data, keyData.mv_size) << "\"" << LOG_ENDLINE; objData.mv_size = 0; objData.mv_data = nullptr; rc = mdb_get(txn, dbi, &keyData, &objData); if (rc != 0) { LOG_CERR(error) << "Could not get object," << " errMess=\"" << mdb_strerror(rc) << "\"" << LOG_ENDLINE; mdb_txn_abort(txn); return (nullptr); } const unsigned char *ucp = reinterpret_cast(objData.mv_data); // return object data, skip 4-byte flag prefix OMEstring *data = new OMEstring(ucp + sizeof(uint32_t), objData.mv_size - sizeof(uint32_t)); // mdb_dbi_close(LMDB_env, dbi) is intentionally not called rc = mdb_txn_commit(txn); if (rc != 0) { LOG_CERR(error) << "Could not commit transaction," << " errMess=\"" << mdb_strerror(rc) << "\"" << LOG_ENDLINE; return (nullptr); } return (data); } #else /* dbm */ OMEstring *read_object(const OMEstring &key) { datum keyData, resultData; keyData.dptr = const_cast(static_cast(*objectName.value.s)); keyData.dsize = objectName.value.s->length(); resultData = dbm_fetch(PDdbHandle, keyData); if (resultData.dptr == nullptr) { // failed... return (nullptr); } unsigned char *ucp = reinterpret_cast(resultData.dptr); // return object data, skip 4-byte flag prefix OMEstring *data = new OMEstring(ucp + sizeof(uint32_t), resultData.dsize - sizeof(uint32_t)); return (data); } #endif #if USE_LMDB == 1 static int doListObjects(OMEtype &result, OMEtype &args, OMEtype &fromObj, OMEtype &context, OMEtype &user) { MDB_txn *txn; MDB_dbi dbi; MDB_cursor *db_cursor; MDB_val keyData, objData; result.initializeAsType(OME_ARRAY); uint32_t delCount = 0; uint32_t count = 0; // because temporary objects may be deleted as a side effect, // the transaction is not flagged as read-only int rc = mdb_txn_begin(LMDB_env, nullptr, 0, &txn); if (rc != 0) { LOG_CERR(error) << "Could not create transaction" << LOG_ENDLINE; return (-1); } rc = mdb_dbi_open(txn, nullptr, 0, &dbi); if (rc != 0) { LOG_CERR(error) << "database does not exist to list objects" << LOG_ENDLINE; mdb_txn_abort(txn); return (0); } rc = mdb_cursor_open(txn, dbi, &db_cursor); rc = mdb_cursor_get(db_cursor, &keyData, &objData, MDB_FIRST); while (rc == 0) { OMEarray rec; uint32_t flag; const unsigned char *ucp = reinterpret_cast(objData.mv_data); memcpy(&flag, ucp, sizeof(uint32_t)); flag = ntohl(flag); if (flag == TEMPORARY) { // delete rc = mdb_cursor_del(db_cursor, 0); ++delCount; } else { OMEstring *data = new OMEstring(reinterpret_cast(keyData.mv_data), keyData.mv_size); LOG_CERR(trace) << "got key count=" << count + 1 << " len=" << keyData.mv_size << " data=\"" << AS_HEXADECIMAL_BUFFER(keyData.mv_data, keyData.mv_size) << LOG_ENDLINE; rec[0] = data; // take ownership... rec[1] = flag; result[count++] = rec; } rc = mdb_cursor_get(db_cursor, &keyData, &objData, MDB_NEXT); } if (OMEdebugFlag & OMEdebugLogLevel3) { LOG_COUT(info) << "listObjects detects " << count << " active objects in database" << LOG_ENDLINE; if (delCount != 0) { LOG_COUT(info) << "Deleted " << delCount << " temporary objects" << LOG_ENDLINE; } } mdb_cursor_close(db_cursor); // mdb_dbi_close(LMDB_env, dbi) is intentionally not called rc = mdb_txn_commit(txn); if (rc != 0) { LOG_CERR(error) << "Could not commit transaction" << LOG_ENDLINE; } return (count); } #else /* dbm */ static int doListObjects(OMEtype &result, OMEtype &args, OMEtype &fromObj, OMEtype &context, OMEtype &user) { datum key; // "key" is used by dbm_nextkey macro result.initializeAsType(OME_ARRAY); uint32_t delCount = 0; uint32_t count = 0; // NOTE: must use "key" as variable for GDBM-based macro key = dbm_firstkey(PDdbHandle); while (key.dptr != nullptr) { OMEarray rec; uint32_t flag; datum resultData; // fetch flags... resultData = dbm_fetch(PDdbHandle, key); if (resultData.dptr == nullptr) { // failed... continue; } unsigned char *ucp = reinterpret_cast(resultData.dptr); memcpy(&flag, ucp, sizeof(uint32_t)); flag = ntohl(flag); if (flag == TEMPORARY) { // delete dbm_delete(PDdbHandle, key); ++delCount; } else { OMEstring *data = new OMEstring(reinterpret_cast(key.dptr), key.dsize); rec[0] = data; // take ownership... rec[1] = flag; result[count++] = rec; } // NOTE: must use "key" as variable for GDBM-based macro key = dbm_nextkey(PDdbHandle); } if (OMEdebugFlag & OMEdebugLogLevel3) { LOG_COUT(info) << count << " active objects in database" << LOG_ENDLINE; if (delCount != 0) { LOG_COUT(info) << "Deleted " << delCount << " temporary objects" << LOG_ENDLINE; } } return (count); } #endif #if USE_LMDB == 1 static int PDopenDB(const char *fileName) { int rc = mdb_env_create(&LMDB_env); if (rc != 0) { LOG_CERR(error) << "Could not create LMDB environment, rc=" << rc << LOG_ENDLINE; return (-1); } rc = mdb_env_open(LMDB_env, fileName, MDB_NOSUBDIR, 0666); if (rc != 0) { LOG_CERR(error) << "Could not open LMDB environment, rc=" << rc << " filename=\"" << fileName << "\"" << " errMess=\"" << mdb_strerror(rc) << "\"" << LOG_ENDLINE; mdb_env_close(LMDB_env); return (-1); } return (0); } #else /* dbm */ static int PDopenDB(const char *fileName) { PDdbHandle = dbm_open(const_cast(fileName), O_CREAT | O_RDWR, 0600); if (PDdbHandle == nullptr) { return (-1); } return (0); } #endif #if USE_LMDB == 1 static int PDcloseDB() { mdb_env_close(LMDB_env); return (0); } #else /* dbm */ static int PDcloseDB() { dbm_close(PDdbHandle); return (0); } #endif static int doSaveObject(OMEtype &args, OMEtype &fromObj, OMEtype &context, OMEtype &user) { OMEtype &objectName = args[0]; if (objectName.type != OME_STRING) { LOG_CERR(error) << "bad type for object name" << LOG_ENDLINE; return (-1); } OMEtype &objectData = args[1]; if (objectData.type != OME_STRING) { LOG_CERR(error) << "bad type for object data" << LOG_ENDLINE; return (-1); } int rc = save_object(*objectName.value.s, *objectData.value.s); return (rc); } static int doRemoveObject(OMEtype &args, OMEtype &fromObj, OMEtype &context, OMEtype &user) { OMEtype &objectName = args[0]; if (objectName.type != OME_STRING) { LOG_CERR(error) << "bad type for object name" << LOG_ENDLINE; return (-1); } int rc = remove_object(*objectName.value.s); return (rc); } static int doRestoreObject(OMEtype &resultVec, OMEtype &args, OMEtype &fromObj, OMEtype &context, OMEtype &user) { OMEtype &objectName = args[0]; if (objectName.type != OME_STRING) { LOG_CERR(error) << "bad type for object name" << LOG_ENDLINE; return (-1); } OMEstring *data = read_object(*objectName.value.s); if (data == nullptr) { // failed ... LOG_CERR(error) << "object not found" << LOG_ENDLINE; return (-1); } resultVec[0] = data; // take ownership... return (0); } static int OMEobjDBloop() { OMEtype mess, dest, nil; OMEtype methodName, args, fromObj, targetObj, context, user; int rc = PDopenDB(dbFileName); if (rc != 0) { LOG_CERR(error) << "OMEpersistd: could not open database=\"" << dbFileName << "\"" << LOG_ENDLINE; return (rc); // exit... } OMEstring userName("defaultUser"); OMEassoc methodList; OMEassoc *acl = OMEapi::makeACL(userName); int count = 0; while (1) { api = new OMEapi(*acl, addr); rc = api->establishConnection(connectionUserInfo); if (rc == 0) { break; // success.. } delete api; api = nullptr; count += 1; if (count == 3) { LOG_CERR(error) << "OMEpersistd: could not connect to addr=\"" << addr << "\" after attempts=" << count << LOG_ENDLINE; return (2); } #ifdef _WIN32 SleepEx(3 * 1000, FALSE); #else sleep(3); #endif } delete acl; // send "createTemporaryObject"("PersistenceTemporaryService", acl, // thisServiceName, thisObject, pagerServiceName, thisServiceName) // to peerObject; api->getPeerObject(dest); methodName = "createTemporaryObject"; args.initializeAsType(OME_ARRAY); args[0] = "PersistenceTemporaryService"; args[1].initializeAsType(OME_NIL); // nil == default ACL args[2] = thisServiceName; api->getThisObject(args[3]); // name of PersistenceService args[4] = pagerServiceName; // how this process is registered using databaseConnected method below args[5] = thisServiceName; rc = api->invokeMethod(dest, methodName, args); if (rc != 0) { LOG_CERR(error) << "OMEpersistd: could not invoke method=\"" << methodName << "\"" << LOG_ENDLINE; delete api; return (3); } do { rc = api->importInvocation(methodName, args, &fromObj, &targetObj, &context, &user); if (rc != 0) { LOG_CERR(error) << "OMEpersistd: could not retrieve registration oid" << LOG_ENDLINE; delete api; return (3); } // NOTE: we might get unexpected messages from other objects, // such as AnnounceServices, so watch for our "reply" before // proceeding and invoking databaseConnected. //std::cout << "Imported method=" << methodName << "\n"; } while (methodName != "reply"); methodName = "databaseConnected"; args.initializeAsType(OME_ARRAY); args[0] = thisServiceName; dest = pagerServiceName; rc = api->invokeMethod(dest, methodName, args); if (rc != 0) { LOG_CERR(error) << "OMEpersistd: could not invoke method=\"" << methodName << "\"" << LOG_ENDLINE; delete api; return (3); } // perform service... do { rc = api->importInvocation(methodName, args, &fromObj, &targetObj, &context, &user); if (rc != 0) { break; } // validity check: could verify if targetObj == thisObject if (OMEdebugFlag & OMEdebugLogLevel1) { LOG_CERR(trace) << "methodName=\"" << methodName << "\"" << LOG_ENDLINE; } if (*methodName.value.s == "saveObject") { rc = doSaveObject(args, fromObj, context, user); args.initializeAsType(OME_ARRAY); args[0] = rc; rc = api->invokeMethod(fromObj, replyMethodName, args); } else if ((*methodName.value.s == "restoreObject") || (*methodName.value.s == "objectFault")) { OMEtype resultVec(OME_ARRAY); rc = doRestoreObject(resultVec, args, fromObj, context, user); rc = api->invokeMethod(fromObj, replyMethodName, resultVec); } else if (*methodName.value.s == "removeObject") { rc = doRemoveObject(args, fromObj, context, user); args.initializeAsType(OME_ARRAY); args[0] = rc; rc = api->invokeMethod(fromObj, replyMethodName, args); } else if (*methodName.value.s == "stopService") { args.initializeAsType(OME_ARRAY); args[0] = 1; rc = api->invokeMethod(fromObj, replyMethodName, args); rc = 1; } else if (*methodName.value.s == "listObjects") { OMEtype resultVec(OME_ARRAY); rc = doListObjects(resultVec[0], args, fromObj, context, user); rc = api->invokeMethod(fromObj, replyMethodName, resultVec); } else { LOG_CERR(error) << "unrecognized methodName=\"" << methodName << "\"" << LOG_ENDLINE; } if (rc != 0) { break; } } while (static_cast(OMEstopFlag) == 0); delete api; LOG_CERR(info) << "closing database" << LOG_ENDLINE; // always close open database... PDcloseDB(); if ((rc != 1) && (static_cast(OMEstopFlag) == 0)) { LOG_CERR(error) << "OMEpersistd: error, rc=" << rc << LOG_ENDLINE; return (rc); } return (0); } static void usageMess(const char *argv[]) { std::cerr << "usage: " << argv[0] << " [-s] [--name serviceName] [--domain vistaDomain --secret secretPhrase] tcp:addr:port [dbFile=vistaObjects.vdb]\n"; std::cerr << " -s = run as service\n"; std::cerr << " --name serviceName = further qualify service name from\n"; std::cerr << " default of " << REGISTERED_AS << "\n"; std::cerr.flush(); } static void emitVersion(const char *argv[]) { std::cerr << "FARGOS/VISTA OME Persistent Object Database Daemon version " << srcID << "\n"; std::cerr.flush(); } #ifndef _WIN32 // NOTE: this is a signal handler... static void requestStop(int sig, siginfo_t *info, void *extra) { OMEstopFlag = 1; struct sigaction stopSigData; stopSigData.sa_handler = SIG_DFL; stopSigData.sa_flags = 0; sigemptyset(&(stopSigData.sa_mask)); sigaction(sig, &stopSigData, 0); } #endif #ifndef MAIN_FUNCTION_NAME #define MAIN_FUNCTION_NAME main #endif int MAIN_FUNCTION_NAME(int argc, const char *argv[], const char *envp[]) { int i; if (argc < 2) { usageMess(argv); return (1); } if ((argc == 2) && (strcmp(argv[1], "--help") == 0)) { usageMess(argv); return (1); } if ((argc == 2) && (strcmp(argv[1], "--version") == 0)) { emitVersion(argv); return (1); } strcpy(pagerServiceName, OBJECT_PAGER_SERVICE); strcpy(thisServiceName, REGISTERED_AS); OMEinitDebugFlag(); bool runAsService = false; for (i = 1; i < argc; i++) { if (strcmp(argv[i], "-s") == 0) { runAsService = true; OMEdebugFlag = 0; } else if (strcmp(argv[i], "-d") == 0) { if (++i >= argc) { break; } OMEsetDebugFlag(argv[i], 0); } else if (strcmp(argv[i], "+d") == 0) { if (++i >= argc) { break; } OMEsetDebugFlag(argv[i], 1); } else if ((strcmp(argv[i], "-domain") == 0) || (strcmp(argv[i], "--domain") == 0)) { if (++i >= argc) { break; } if (connectionUserInfo.type != OME_ARRAY) { connectionUserInfo.initializeAsType(OME_ARRAY); } connectionUserInfo[0] = argv[i]; // VISTA domain name } else if ((strcmp(argv[i], "-secret") == 0) || (strcmp(argv[i], "--secret") == 0)) { if (++i >= argc) { break; } if (connectionUserInfo.type != OME_ARRAY) { connectionUserInfo.initializeAsType(OME_ARRAY); } connectionUserInfo[1] = argv[i]; // secret // blank argument memset((void *)(argv[i]), '\0', strlen(argv[i])); } else if ((strcmp(argv[i], "-name") == 0) || (strcmp(argv[i], "--name") == 0)) { if (++i >= argc) { break; } if ((strlen(argv[i]) + strlen(pagerServiceName)) > (sizeof(pagerServiceName) - 16)) { std::cerr << "service name too long\n"; return (2); } strcat(pagerServiceName, "/"); strcat(pagerServiceName, argv[i]); strcat(thisServiceName, ":"); strcat(thisServiceName, argv[i]); } else { break; } } if (i == argc) { usageMess(argv); return (1); } if (OMEdebugFlag & OMEdebugLogLevel3) { std::cerr << "FARGOS/VISTA OME Persistent Object Database Daemon\n"; std::cerr << "Copyright (C) 1999 - 2020 FARGOS Development, LLC. All rights reserved.\n"; std::cerr << "Using " << pagerServiceName << " registered as " << thisServiceName << "\n"; std::cerr.flush(); } #ifdef _WIN32 initWinsock(); OMEcreateShutdownEvent(); #endif OMEloadVersion1Encodings(); OMEregisterStandardSocketSchemes(); if (memccpy(addr, argv[i], '\0', sizeof(addr) - 4) == nullptr) { addr[sizeof(addr) - 3] = '\0'; // save space for ",cb" } strcat(addr, ",cb"); ++i; dbFileName = (argc > i) ? argv[i] : "vistaObjects.vdb"; replyMethodName = "reply"; #ifndef _WIN32 // Set up for graceful termination, which may include // writing out symbolic thread Id table struct sigaction stopSigData; stopSigData.sa_sigaction = requestStop; stopSigData.sa_flags = SA_SIGINFO; sigemptyset(&(stopSigData.sa_mask)); sigaction(SIGTERM, &stopSigData, 0); sigaction(SIGINT, &stopSigData, 0); #endif if (runAsService == false) { OMEobjDBloop(); return (0); } // run as a service... #ifdef _WIN32 OMErunAsWindowsService("OMEpersistd", OMEobjDBloop); ExitProcess(0); #else OMEobjDBloop(); #endif return (0); // normal stop... } /* vim: set expandtab shiftwidth=4 tabstop=4: */