AJA NTV2 SDK  18.1.0.2439
NTV2 SDK 18.1.0.2439
ntv2nubaccess.cpp
Go to the documentation of this file.
1 /* SPDX-License-Identifier: MIT */
7 #include "ajatypes.h"
8 #include "ntv2utils.h"
9 #include "ntv2nubaccess.h"
10 #include "ntv2publicinterface.h"
11 #include "ntv2version.h"
12 #include "ajabase/system/debug.h"
13 #include "ajabase/common/common.h"
15 #include "ajabase/system/atomic.h"
16 #include "ajabase/system/info.h" // for AJASystemInfo
17 #include <iomanip>
18 #if !defined(NTV2_PREVENT_PLUGIN_LOAD)
20  #include "ajabase/system/thread.h"
21  #include <fstream>
22  #include "mbedtls/x509.h"
23  #include "mbedtls/error.h"
24  #include "mbedtls/md.h"
25  #include "mbedtls/ssl.h"
26 #endif // defined(NTV2_PREVENT_PLUGIN_LOAD)
27 #if defined(AJAMac)
28  #include <CoreFoundation/CoreFoundation.h>
29  #include <dlfcn.h>
30  #define DLL_EXTENSION ".dylib"
31  #define PATH_DELIMITER "/"
32  #define FIRMWARE_FOLDER "Firmware"
33 #elif defined(AJALinux)
34  #include <dlfcn.h>
35  #define DLL_EXTENSION ".so"
36  #define PATH_DELIMITER "/"
37  #define FIRMWARE_FOLDER "firmware"
38 #elif defined(MSWindows)
39  #define DLL_EXTENSION ".dll"
40  #define PATH_DELIMITER "\\"
41  #define FIRMWARE_FOLDER "Firmware"
42 #elif defined(AJABareMetal)
43  #define DLL_EXTENSION ".so"
44  #define PATH_DELIMITER "/"
45  #define FIRMWARE_FOLDER "firmware"
46 #endif
47 #define SIG_EXTENSION ".sig"
48 
49 using namespace std;
50 
51 #define INSTP(_p_) xHEX0N(uint64_t(_p_),16)
52 #define NBFAIL(__x__) AJA_sERROR (AJA_DebugUnit_RPCClient, INSTP(this) << "::" << AJAFUNC << ": " << __x__)
53 #define NBWARN(__x__) AJA_sWARNING(AJA_DebugUnit_RPCClient, INSTP(this) << "::" << AJAFUNC << ": " << __x__)
54 #define NBNOTE(__x__) AJA_sNOTICE (AJA_DebugUnit_RPCClient, INSTP(this) << "::" << AJAFUNC << ": " << __x__)
55 #define NBINFO(__x__) AJA_sINFO (AJA_DebugUnit_RPCClient, INSTP(this) << "::" << AJAFUNC << ": " << __x__)
56 #define NBDBG(__x__) AJA_sDEBUG (AJA_DebugUnit_RPCClient, INSTP(this) << "::" << AJAFUNC << ": " << __x__)
57 #define NBCFAIL(__x__) AJA_sERROR (AJA_DebugUnit_RPCClient, AJAFUNC << ": " << __x__)
58 #define NBCWARN(__x__) AJA_sWARNING(AJA_DebugUnit_RPCClient, AJAFUNC << ": " << __x__)
59 #define NBCNOTE(__x__) AJA_sNOTICE (AJA_DebugUnit_RPCClient, AJAFUNC << ": " << __x__)
60 #define NBCINFO(__x__) AJA_sINFO (AJA_DebugUnit_RPCClient, AJAFUNC << ": " << __x__)
61 #define NBCDBG(__x__) AJA_sDEBUG (AJA_DebugUnit_RPCClient, AJAFUNC << ": " << __x__)
62 #define NBSFAIL(__x__) AJA_sERROR (AJA_DebugUnit_RPCServer, AJAFUNC << ": " << __x__)
63 #define NBSWARN(__x__) AJA_sWARNING(AJA_DebugUnit_RPCServer, AJAFUNC << ": " << __x__)
64 #define NBSNOTE(__x__) AJA_sNOTICE (AJA_DebugUnit_RPCServer, AJAFUNC << ": " << __x__)
65 #define NBSINFO(__x__) AJA_sINFO (AJA_DebugUnit_RPCServer, AJAFUNC << ": " << __x__)
66 #define NBSDBG(__x__) AJA_sDEBUG (AJA_DebugUnit_RPCServer, AJAFUNC << ": " << __x__)
67 
68 #define PLGFAIL(__x__) AJA_sERROR (AJA_DebugUnit_Plugins, AJAFUNC << ": " << __x__)
69 #define PLGWARN(__x__) AJA_sWARNING(AJA_DebugUnit_Plugins, AJAFUNC << ": " << __x__)
70 #define PLGNOTE(__x__) AJA_sNOTICE (AJA_DebugUnit_Plugins, AJAFUNC << ": " << __x__)
71 #define PLGINFO(__x__) AJA_sINFO (AJA_DebugUnit_Plugins, AJAFUNC << ": " << __x__)
72 #define PLGDBG(__x__) AJA_sDEBUG (AJA_DebugUnit_Plugins, AJAFUNC << ": " << __x__)
73 
74 #define P_FAIL(__x__) do \
75  { \
76  ostringstream _os_; \
77  _os_ << AJAFUNC << ": " << __x__; \
78  if (useStdout()) \
79  cout << "## ERROR: " << _os_.str() << endl; \
80  AJA_sERROR (AJA_DebugUnit_Plugins, _os_.str()); \
81  errMsg = _os_.str(); \
82  } while (false)
83 #define P_WARN(__x__) if (useStdout()) cout << "## WARNING: " << AJAFUNC << ": " << __x__ << endl; \
84  AJA_sWARNING(AJA_DebugUnit_Plugins, AJAFUNC << ": " << __x__)
85 #define P_NOTE(__x__) if (useStdout()) cout << "## NOTE: " << AJAFUNC << ": " << __x__ << endl; \
86  AJA_sNOTICE (AJA_DebugUnit_Plugins, AJAFUNC << ": " << __x__)
87 #define P_INFO(__x__) if (useStdout()) cout << "## INFO: " << AJAFUNC << ": " << __x__ << endl; \
88  AJA_sINFO (AJA_DebugUnit_Plugins, AJAFUNC << ": " << __x__)
89 #define P_DBG(__x__) if (useStdout()) cout << "## DEBUG: " << AJAFUNC << ": " << __x__ << endl; \
90  AJA_sDEBUG (AJA_DebugUnit_Plugins, AJAFUNC << ": " << __x__)
91 //#define _DEBUGSTATS_ // Define this to log above construct/destruct & open/close tallies
92 #if defined(_DEBUGSTATS_)
93  #define PDBGX(__x__) AJA_sDEBUG (AJA_DebugUnit_Plugins, INSTP(this) << "::" << AJAFUNC << ": " << __x__)
94 #else
95  #define PDBGX(__x__)
96 #endif
97 
98 
99 // Stats
100 uint32_t gBaseConstructCount(0); // Number of NTV2RPCBase constructor calls made
101 uint32_t gBaseDestructCount(0); // Number of NTV2RPCBase destructor calls made
102 uint32_t gClientConstructCount(0); // Number of NTV2RPCClient constructor calls made
103 uint32_t gClientDestructCount(0); // Number of NTV2RPCClient destructor calls made
104 uint32_t gServerConstructCount(0); // Number of NTV2RPCServer constructor calls made
105 uint32_t gServerDestructCount(0); // Number of NTV2RPCServer destructor calls made
106 uint32_t gLoaderConstructCount(0); // Number of NTV2PluginLoader constructor calls made
107 uint32_t gLoaderDestructCount(0); // Number of NTV2PluginLoader destructor calls made
108 uint32_t gPluginConstructCount(0); // Number of NTV2Plugin constructor calls made
109 uint32_t gPluginDestructCount(0); // Number of NTV2Plugin destructor calls made
110 
111 
112 string NTV2Dictionary::valueForKey (const string & inKey) const
113 {
114  DictConstIter it(mDict.find(inKey));
115  if (it == mDict.end())
116  return "";
117  return it->second;
118 }
119 
120 uint16_t NTV2Dictionary::u16ValueForKey (const string & inKey, const uint16_t inDefault) const
121 {
122  string str(valueForKey(inKey));
123  if (str.empty())
124  return inDefault;
125  if (str.find("0x") == 0 || str.find("0X") == 0)
126  {
127  str.erase(0,2);
128  if (str.empty())
129  return inDefault;
130  return uint16_t(aja::stoul(str, AJA_NULL, 16));
131  }
132  if (str.find("x") == 0 || str.find("X") == 0)
133  {
134  str.erase(0,1);
135  if (str.empty())
136  return inDefault;
137  return uint16_t(aja::stoul(str, AJA_NULL, 16));
138  }
139  if (str.find("o") == 0 || str.find("O") == 0)
140  {
141  str.erase(0,1);
142  if (str.empty())
143  return inDefault;
144  return uint16_t(aja::stoul(str, AJA_NULL, 8));
145  }
146  if (str.find("b") == 0 || str.find("B") == 0)
147  {
148  str.erase(0,1);
149  if (str.empty())
150  return inDefault;
151  return uint16_t(aja::stoul(str, AJA_NULL, 2));
152  }
153  return uint16_t(aja::stoul(str, AJA_NULL, 10));
154 }
155 
156 ostream & NTV2Dictionary::Print (ostream & oss, const bool inCompact) const
157 {
158  if (inCompact)
159  for (DictConstIter it(mDict.begin()); it != mDict.end(); )
160  {
161  const string & key(it->first), val(it->second), quote(val.find(' ') != string::npos ? "'" : "");
162  oss << key << "=" << quote << val << quote;
163  if (++it != mDict.end())
164  oss << " ";
165  }
166  else if (empty())
167  oss << "0 entries";
168  else
169  {
170  const int kyWdth(int(largestKeySize()+0)), valWdth(int(largestValueSize()+0));
171  oss << string(size_t(kyWdth), '-') << " " << string(size_t(valWdth), '-') << endl;
172  for (DictConstIter it(mDict.begin()); it != mDict.end(); )
173  {
174  const string & key(it->first), val(it->second);
175  oss << std::setw(kyWdth) << key << " : " << val;
176  if (++it != mDict.end())
177  oss << endl;
178  }
179  }
180  return oss;
181 }
182 
183 bool NTV2Dictionary::deserialize (const string & inStr)
184 {
185  size_t badKVPairs(0), insertFailures(0);
186  clear();
187  const NTV2StringList keyValPairs (aja::split(inStr, "\n"));
188  for (NTV2StringListConstIter it(keyValPairs.begin()); it != keyValPairs.end(); ++it)
189  {
190  const NTV2StringList keyValPair (aja::split(*it, "\t"));
191  if (keyValPair.size() != 2)
192  {badKVPairs++; continue;}
193  const string k(keyValPair.at(0)), v(keyValPair.at(1));
194  if (!insert(k, v))
195  insertFailures++;
196  }
197  return !empty() && !badKVPairs && !insertFailures;
198 }
199 
200 bool NTV2Dictionary::serialize (string & outStr) const
201 {
202  outStr.clear();
203  ostringstream oss;
204  for (DictConstIter it(mDict.begin()); it != mDict.end(); )
205  {
206  oss << it->first << "\t" << it->second;
207  if (++it != mDict.end())
208  oss << "\n";
209  }
210  outStr = oss.str();
211  return !outStr.empty();
212 }
213 
215 {
216  NTV2StringSet result;
217  for (DictConstIter it(mDict.begin()); it != mDict.end(); ++it)
218  result.insert(it->first);
219  return result;
220 }
221 
223 {
224  size_t result(0);
225  for (DictConstIter it(mDict.begin()); it != mDict.end(); ++it)
226  if (it->first.length() > result)
227  result = it->first.length();
228  return result;
229 }
230 
232 {
233  size_t result(0);
234  for (DictConstIter it(mDict.begin()); it != mDict.end(); ++it)
235  if (it->second.length() > result)
236  result = it->second.length();
237  return result;
238 }
239 
240 bool NTV2Dictionary::insert (const string & inKey, const string & inValue)
241 {
242  if (inKey.empty())
243  return false;
244  if (inKey.find("\t") != string::npos)
245  return false;
246  if (inKey.find("\n") != string::npos)
247  return false;
248  if (inValue.find("\t") != string::npos)
249  return false;
250  if (inValue.find("\n") != string::npos)
251  return false;
252  try {
253  mDict[inKey] = inValue; // Insert or update
254  } catch (const bad_alloc &) {
255  return false; // no memory
256  } catch (...) {
257  return false; // failed
258  }
259  return true;
260 }
261 
263 {
264  size_t numUpdated(0);
265  for (DictConstIter it(inDict.mDict.begin()); it != inDict.mDict.end(); ++it)
266  if (hasKey(it->first))
267  {mDict[it->first] = it->second; numUpdated++;}
268  return numUpdated;
269 }
270 
272 {
273  size_t numAdded(0);
274  for (DictConstIter it(inDict.mDict.begin()); it != inDict.mDict.end(); ++it)
275  if (!hasKey(it->first))
276  {mDict[it->first] = it->second; numAdded++;}
277  return numAdded;
278 }
279 
280 
282 {
283  Reset(inSpec);
284 }
285 
286 void NTV2DeviceSpecParser::Reset (const string inSpec)
287 {
288  mErrors.clear();
289  mResult.clear();
290  mQueryParams.clear();
291  mPos = 0;
292  mSpec = inSpec;
293  if (!mSpec.empty())
294  Parse(); // Go ahead and parse it
295 }
296 
297 string NTV2DeviceSpecParser::Resource (const bool inStripLeadSlash) const
298 {
299  string rsrc (Result(kConnectParamResource));
300  if (rsrc.empty())
301  return rsrc;
302  if (!inStripLeadSlash)
303  return rsrc;
304  if (rsrc.at(0) == '/')
305  rsrc.erase(0,1);
306  return rsrc;
307 }
308 
310 {
311  string result(Host()), port(Port());
312  if (result.empty())
313  return "";
314  if (!port.empty())
315  result += ":" + port;
316  return result;
317 }
318 
319 void NTV2DeviceSpecParser::Parse (void)
320 {
321  // A run of 3 consecutive letters that match "ntv" -- probably a scheme
322  // A run of 1 or 2 decimal digits -- probably a local device index number
323  // "0X" or "0x":
324  // - maybe a hexadecimal 32-bit value -- a local device ID
325  // - maybe a hexadecimal 64-bit value -- a local device serial number
326  // A run of 8 or 9 alphanumeric chars -- probably a local device serial number
327  ostringstream err;
328  string tokDevID, tokIndexNum, tokScheme, tokSerial, tokModelName, tokIPV4, tokPortNum;
329  size_t posDevID(0), posIndexNum(0), posScheme(0), posSerial(0), posModelName(0), posNetAddr(0);
330  bool isSerial(ParseSerialNum(posSerial, tokSerial)), isScheme(ParseScheme(posScheme, tokScheme));
331  bool isIndexNum(ParseDecNumber(posIndexNum, tokIndexNum)), isDeviceID(ParseDeviceID(posDevID, tokDevID));
332  bool isModelName(ParseModelName(posModelName, tokModelName));
333  bool isIPV4Port(ParseHostAddressAndPortNumber(posNetAddr, tokIPV4, tokPortNum));
334  if (isScheme && tokScheme == kLegalSchemeNTV2Local)
335  { // Re-parse serial#, index#, deviceID, modelName from just past "://"...
336  posDevID = posIndexNum = posSerial = posModelName = posScheme;
337  isSerial = ParseSerialNum(posSerial, tokSerial);
338  isIndexNum = ParseDecNumber(posIndexNum, tokIndexNum);
339  isDeviceID = ParseDeviceID(posDevID, tokDevID);
340  isModelName = ParseModelName(posModelName, tokModelName);
341  // Check for query...
342  size_t posQuery(0);
343  if (isDeviceID) posQuery = posDevID;
344  else if (isSerial) posQuery = posSerial;
345  else if (isModelName) posQuery = posModelName;
346  else if (isIndexNum) posQuery = posIndexNum;
347  if (posQuery)
348  {
349  NTV2Dictionary params;
350  if (ParseQuery(posQuery, params))
351  {
352  mResult.insert(kConnectParamQuery, DeviceSpec().substr(mPos, posQuery-mPos+1));
353  mQueryParams = params;
354  mPos = posQuery;
355  if (isDeviceID) posDevID = mPos;
356  else if (isSerial) posSerial = mPos;
357  else if (isModelName) posModelName = mPos;
358  else if (isIndexNum) posIndexNum = mPos;
359  }
360  }
361  }
362  do
363  {
364  if (isModelName)
365  {
366  mPos = posModelName;
368  mResult.insert(kConnectParamDevModel, tokModelName);
369  break;
370  }
371  if (isSerial)
372  { // Final serial number checks...
373  bool converted(false);
374  mPos = posSerial;
375  if (tokSerial.length() == 18) // 64-bit hex value?
376  {
377  // Convert numeric serial number into character string...
378  const bool hasLeading0X (tokSerial.find("0X") == 0 || tokSerial.find("0x") == 0);
379  const string hex64(tokSerial.substr(hasLeading0X ? 2 : 0, 16));
380  const ULWord64 serNum64(aja::stoull(hex64, AJA_NULL, 16));
381  string serTxt; // (CNTV2Card::SerialNum64ToString(serNum64));
382  for (size_t ndx(0); ndx < 8; ndx++)
383  serTxt += char(serNum64 >> ((7-ndx)*8));
384  //cerr << "Converted '" << tokSerial << "' into '" << serTxt << "'" << endl;
385  tokSerial = serTxt;
386  converted = true;
387  }
388  // Check for illegal characters in serial number:
389  for (size_t ndx(0); ndx < tokSerial.length(); ndx++)
390  { char ch(tokSerial.at(ndx));
391  if ( ! ( ( (ch >= '0') && (ch <= '9') ) ||
392  ( (ch >= 'A') && (ch <= 'Z') ) ||
393  ( (ch >= 'a') && (ch <= 'z') ) ||
394  (ch == ' ') || (ch == '-') ) )
395  {
396  err << "Illegal serial number character '" << (ch ? ch : '?') << "' (" << xHEX0N(UWord(ch),2) << ")";
397  AddError(err.str());
398  mPos -= converted ? 16 : 8; mPos += ndx * (converted ? 2 : 1) + (converted ? 1 : 0);
399  break;
400  }
401  }
402  mResult.insert(kConnectParamDevSerial, tokSerial);
404  break;
405  }
406  if (isDeviceID)
407  {
408  mPos = posDevID;
409  mResult.insert(kConnectParamDevID, tokDevID);
411  break;
412  }
413  if (isIndexNum)
414  {
415  if (posIndexNum < SpecLength())
416  { // Check if extra chars past index num is dotted quad:
417  if (isIPV4Port && !tokIPV4.empty())
418  {
419  mPos = posNetAddr;
420  mResult.insert(kConnectParamScheme, "ntv2nubrpclib");
421  mResult.insert(kConnectParamHost, tokIPV4);
422  if (!tokPortNum.empty())
423  mResult.insert(kConnectParamPort, tokPortNum);
424  break;
425  }
426  err << "Extra characters past index number";
427  AddError(err.str());
428  break;
429  }
430  mPos = posIndexNum;
431  mResult.insert(kConnectParamDevIndex, tokIndexNum);
433  break;
434  }
435  if (!isScheme || (isScheme && tokScheme == kLegalSchemeNTV2Local))
436  { // No such local device
437  err << "Invalid local device specification";
438  AddError(err.str());
439  mPos += isScheme ? 12 : 0;
440  break;
441  }
442  if (isScheme)
443  { // Continue parsing URLspec...
444  mPos = posScheme;
445  // "xxxx://swdevice/?"
446  // "nosharedmemory"
447  // "&supportlog=file%3A%2F%2F%2FUsers%2Fdemo%2FDesktop%2FAJAWatcherSupport.log"
448  // "&fbinit=file%3A%2F%2F%2FUsers%2Fdemo%2FDesktop%2FSDRAMsnapshot.dat");
449  // Host[port]/[resource[?query]]
450  size_t posURL(posScheme), posRsrc(0);
451  string host, port, rsrcPath;
452  if (!ParseHostAddressAndPortNumber(posURL, host, port))
453  {mPos = posURL; AddError("Bad host address or port number"); break;}
454  mPos = posURL;
455  mResult.insert(kConnectParamScheme, tokScheme);
456  mResult.insert(kConnectParamHost, host);
457  if (!port.empty())
458  mResult.insert(kConnectParamPort, port);
459 
460  // Parse resource path...
461  posRsrc = mPos;
462  if (ParseResourcePath(posRsrc, rsrcPath))
463  {mPos = posRsrc; mResult.insert(kConnectParamResource, rsrcPath);}
464  // Parse query...
465  size_t posQuery(mPos);
466  NTV2Dictionary params;
467  if (ParseQuery(posQuery, params))
468  {
469  mResult.insert(kConnectParamQuery, DeviceSpec().substr(mPos, posQuery-mPos+1));
470  mQueryParams = params;
471  mPos = posQuery;
472  }
473  }
474  } while (false); // Once thru
475  if (mPos < SpecLength())
476  {err << "Parser failed at character position " << DEC(mPos); AddError(err.str());}
477  #if defined(_DEBUG)
478  ostringstream oss;
479  if (Successful())
480  { oss << "NTV2DeviceSpecParser::Parse success: '" << DeviceSpec() << "' -- ";
481  Print(oss);
483  }
484  else
485  { oss << "NTV2DeviceSpecParser::Parse failed: ";
486  PrintErrors(oss);
488  }
489  #endif // defined(_DEBUG)
490 } // Parse
491 
492 ostream & NTV2DeviceSpecParser::Print (ostream & oss, const bool inDumpResults) const
493 {
494  if (IsLocalDevice())
495  oss << "local device";
496  else if (HasScheme())
497  oss << "device '" << Scheme() << "'";
498  else
499  oss << "device";
500  if (HasResult(kConnectParamDevSerial))
501  oss << " serial '" << DeviceSerial() << "'";
502  else if (HasResult(kConnectParamDevModel))
503  oss << " model '" << DeviceModel() << "'";
504  else if (HasResult(kConnectParamDevID))
505  oss << " ID " << Result(kConnectParamDevID);
506  else if (HasResult(kConnectParamDevIndex))
507  oss << " " << DeviceIndex();
508  if (HasResult(kConnectParamHost))
509  oss << " host '" << Result(kConnectParamHost) << "'";
510  if (HasResult(kConnectParamPort))
511  oss << " port " << Result(kConnectParamPort);
512  if (HasResult(kConnectParamResource))
513  oss << " resource '" << Result(kConnectParamResource) << "'";
514  if (HasResult(kConnectParamQuery))
515  oss << " query '" << Result(kConnectParamQuery) << "'";
516  if (inDumpResults)
517  {oss << endl; Results().Print(oss, /*compact?*/false);}
518  return oss;
519 }
520 
521 string NTV2DeviceSpecParser::MakeDeviceSpec (const bool urlEncodeQuery) const
522 {
523  if (!Successful())
524  return "";
525  ostringstream result;
526  if (IsLocalDevice())
527  {
528  result << "ntv2local://";
529  if (HasResult(kConnectParamDevSerial))
530  result << DeviceSerial();
531  else if (HasResult(kConnectParamDevModel))
532  result << DeviceModel();
533  else if (HasResult(kConnectParamDevID))
534  result << DeviceID();
535  else if (HasResult(kConnectParamDevIndex))
536  result << DeviceIndex();
537  else
538  return "";
539  return result.str();
540  }
541  result << Scheme() << "://";
542  if (HasResult(kConnectParamHost))
543  result << Result(kConnectParamHost);
544  if (HasResult(kConnectParamPort))
545  result << ":" << Result(kConnectParamPort);
546  result << Result(kConnectParamResource);
547  if (HasQueryParams())
548  {
549  string q (MakeQueryString(urlEncodeQuery));
550  if (!q.empty())
551  result << "?" << q;
552  }
553  return result.str();
554 }
555 
556 string NTV2DeviceSpecParser::MakeQueryString (const bool urlEncode) const
557 {
558  if (!Successful())
559  return "";
560  string result;
561  MakeQueryString (mQueryParams, result);
562  return result;
563 }
564 
566 {
567  ostringstream oss;
568  Print(oss);
569  return oss.str();
570 }
571 
573 {
574  string devIDStr (Result(kConnectParamDevID));
575  if (devIDStr.find("0X") != string::npos)
576  devIDStr.erase(0,2); // Delete "0x"
577  ULWord u32 = ULWord(aja::stoull(devIDStr, AJA_NULL, 16));
578  return NTV2DeviceID(u32);
579 }
580 
582 {
583  string devIDStr (Result(kConnectParamDevIndex));
584  UWord u16 = UWord(aja::stoul(devIDStr));
585  return u16;
586 }
587 
588 ostream & NTV2DeviceSpecParser::PrintErrors (ostream & oss) const
589 {
590  oss << DEC(ErrorCount()) << (ErrorCount() == 1 ? " error" : " errors") << (HasErrors() ? ":" : "");
591  if (HasErrors())
592  {
593  oss << endl
594  << DeviceSpec() << endl
595  << string(mPos ? mPos : 0,' ') << "^" << endl;
596  for (size_t num(0); num < ErrorCount(); )
597  {
598  oss << Error(num);
599  if (++num < ErrorCount())
600  oss << endl;
601  }
602  }
603  return oss;
604 }
605 
606 bool NTV2DeviceSpecParser::ParseHexNumber (size_t & pos, string & outToken)
607 {
608  outToken.clear();
609  string tokHexNum;
610  while (pos < SpecLength())
611  {
612  const char ch(CharAt(pos));
613  if (tokHexNum.length() == 0)
614  {
615  if (ch != '0')
616  break;
617  ++pos; tokHexNum = ch;
618  }
619  else if (tokHexNum.length() == 1)
620  {
621  if (ch != 'x' && ch != 'X')
622  break;
623  ++pos; tokHexNum += ch;
624  }
625  else
626  {
627  if (!IsHexDigit(ch))
628  break;
629  ++pos; tokHexNum += ch;
630  }
631  }
632  if (tokHexNum.length() > 2) // At least 3 chars
633  {aja::upper(tokHexNum); outToken = tokHexNum;} // Force upper-case hex
634  return !outToken.empty();
635 }
636 
637 bool NTV2DeviceSpecParser::ParseDecNumber (size_t & pos, string & outToken)
638 {
639  outToken.clear();
640  string tokDecNum;
641  while (pos < SpecLength())
642  {
643  const char ch(CharAt(pos));
644  if (!IsDecimalDigit(ch))
645  break;
646  ++pos;
647  if (ch != '0' || tokDecNum != "0") // This prevents accumulating more than one leading zero
648  tokDecNum += ch;
649  }
650  if (tokDecNum.length() > 0) // At least 1 char
651  outToken = tokDecNum;
652  return !outToken.empty();
653 }
654 
655 bool NTV2DeviceSpecParser::ParseAlphaNum (size_t & pos, string & outToken, const std::string & inOtherChars)
656 { // Run of letters and/or digits, but must start with letter
657  outToken.clear();
658  string tokAlphaNum;
659  while (pos < SpecLength())
660  {
661  const char ch(CharAt(pos));
662  if (!IsLetter(ch) && !IsDecimalDigit(ch) && inOtherChars.find(ch) == string::npos)
663  break; // Break if not letter/digit
664  if (tokAlphaNum.empty() && !IsLetter(ch))
665  break; // Didn't start with letter!
666  ++pos; tokAlphaNum += ch;
667  }
668  if (tokAlphaNum.length() > 0)
669  outToken = tokAlphaNum;
670  return !outToken.empty();
671 }
672 
673 bool NTV2DeviceSpecParser::ParseAlphaNumeric (size_t & pos, string & outToken, const std::string & inOtherChars)
674 { // Run of letters and/or digits (and can start with either)
675  outToken.clear();
676  string tokAlphaNum;
677  while (pos < SpecLength())
678  {
679  const char ch(CharAt(pos));
680  if (!IsLetter(ch) && !IsDecimalDigit(ch) && inOtherChars.find(ch) == string::npos)
681  break;
682  ++pos; tokAlphaNum += ch;
683  }
684  if (tokAlphaNum.length() > 0)
685  outToken = tokAlphaNum;
686  return !outToken.empty();
687 }
688 
689 bool NTV2DeviceSpecParser::ParseScheme (size_t & pos, string & outToken)
690 {
691  outToken.clear();
692  string rawScheme, tokScheme;
693  while (ParseAlphaNum(pos, rawScheme))
694  {
695  tokScheme = rawScheme;
696  char ch(CharAt(pos));
697  if (ch != ':')
698  break;
699  ++pos; tokScheme += ch;
700 
701  ch = CharAt(pos);
702  if (ch != '/')
703  break;
704  ++pos; tokScheme += ch;
705 
706  ch = CharAt(pos);
707  if (ch != '/')
708  break;
709  ++pos; tokScheme += ch;
710  break;
711  }
712  if (tokScheme.find("://") != string::npos) // Contains "://"
713  {aja::lower(rawScheme); outToken = rawScheme;} // Force lower-case
714  return !outToken.empty();
715 }
716 
717 bool NTV2DeviceSpecParser::ParseSerialNum (size_t & pos, string & outToken)
718 {
719  outToken.clear();
720  string tokAlphaNum, tokHexNum;
721  size_t origPos(pos), posAlphaNum(pos), posHexNum(pos);
722  do
723  {
724  while (posAlphaNum < SpecLength())
725  {
726  const char ch(CharAt(posAlphaNum));
727  if (!IsUpperLetter(ch) && !IsLowerLetter(ch) && !IsDecimalDigit(ch) && ch != '-' && ch != ' ')
728  break;
729  ++posAlphaNum; tokAlphaNum += ch;
730  }
731  if (tokAlphaNum.length() < 2) // At least 2 alphanum chars
732  {tokAlphaNum.clear(); break;}
733  if (tokAlphaNum.length() == 8 || tokAlphaNum.length() == 9)
734  {pos = posAlphaNum; outToken = tokAlphaNum; break;}
735 
736  if (ParseHexNumber(posHexNum, tokHexNum))
737  if (tokHexNum.length() == 18) // 64-bit value!
738  {pos = posHexNum; outToken = tokHexNum;}
739  } while (false);
740  if (tokAlphaNum == "ntv2kona1" || tokAlphaNum == "ntv2vkona") // HACK! Can't open 'ntv2kona1' plugin without this hack!
741  {outToken.clear(); pos = origPos; return false;} // ('ntv2kona1' looks like a serial number!)
742  return !outToken.empty();
743 }
744 
745 bool NTV2DeviceSpecParser::ParseDeviceID (size_t & pos, string & outToken)
746 {
747  outToken.clear();
748  string tokHexNum;
749  if (!ParseHexNumber(pos, tokHexNum))
750  return false;
751  if (tokHexNum.length() != 10)
752  return false;
753  aja::upper(tokHexNum); // Fold to upper case
754 
755  // Check if it matches a known supported NTV2DeviceID...
757  NTV2StringSet devIDStrs;
758  for (NTV2DeviceIDSetConstIter it(allDevIDs.begin()); it != allDevIDs.end(); ++it)
759  {
760  ostringstream devID; devID << xHEX0N(*it,8);
761  string devIDStr(devID.str());
762  aja::upper(devIDStr);
763  devIDStrs.insert(devIDStr);
764  } // for each known/supported NTV2DeviceID
765  if (devIDStrs.find(tokHexNum) != devIDStrs.end())
766  outToken = tokHexNum; // Valid!
767  return !outToken.empty();
768 }
769 
770 bool NTV2DeviceSpecParser::ParseModelName (size_t & pos, string & outToken)
771 {
772  outToken.clear();
773  string tokName;
774  if (!ParseAlphaNum(pos, tokName, " "))
775  return false;
776  aja::lower(tokName); // Fold to lower case
777 
778  // Check if it matches a known supported device model name...
780  NTV2StringSet modelNames;
781  for (NTV2DeviceIDSetConstIter it(allDevIDs.begin()); it != allDevIDs.end(); ++it)
782  {
783  string modelName(::NTV2DeviceIDToString(*it));
784  aja::lower(modelName);
785  modelNames.insert(modelName);
786  } // for each known/supported NTV2DeviceID
787  if (modelNames.find(tokName) != modelNames.end())
788  outToken = tokName; // Valid!
789  return !outToken.empty();
790 }
791 
792 bool NTV2DeviceSpecParser::ParseDNSName (size_t & pos, string & outDNSName)
793 {
794  outDNSName.clear();
795  string dnsName, name;
796  size_t dnsPos(pos);
797  char ch(0);
798  while (ParseAlphaNum(dnsPos, name, "_-")) // also allow '_' and '-'
799  {
800  if (!dnsName.empty())
801  dnsName += '.';
802  dnsName += name;
803  ch = CharAt(dnsPos);
804  if (ch != '.')
805  break;
806  ++dnsPos;
807  }
808  if (!dnsName.empty())
809  pos = dnsPos;
810  outDNSName = dnsName;
811  return !outDNSName.empty();
812 }
813 
814 bool NTV2DeviceSpecParser::ParseIPv4Address (size_t & pos, string & outIPv4)
815 {
816  outIPv4.clear();
817  NTV2StringList ipv4Name;
818  string num;
819  size_t ipv4Pos(pos);
820  char ch(0);
821  while (ParseDecNumber(ipv4Pos, num))
822  {
823  ipv4Name.push_back(num);
824  ch = CharAt(ipv4Pos);
825  if (ch != '.')
826  break;
827  ++ipv4Pos;
828  }
829  if (ipv4Name.size() == 4)
830  pos = ipv4Pos;
831  outIPv4 = aja::join(ipv4Name, ".");
832  return ipv4Name.size() == 4;
833 }
834 
835 bool NTV2DeviceSpecParser::ParseHostAddressAndPortNumber (size_t & pos, string & outAddr, string & outPort)
836 {
837  outAddr.clear(); outPort.clear();
838  // Look for a DNSName or an IPv4 dotted quad...
839  string dnsName, ipv4, port;
840  size_t dnsPos(pos), ipv4Pos(pos), portPos(0);
841  bool isDNS(ParseDNSName(dnsPos, dnsName)), isIPv4(ParseIPv4Address(ipv4Pos, ipv4));
842  if (!isDNS && !isIPv4)
843  {pos = dnsPos < ipv4Pos ? ipv4Pos : dnsPos; return false;}
844  // NOTE: It's possible to have both isIPv4 && isDNS true -- in this case, isIPv4 takes precedence:
845  if (isIPv4)
846  {outAddr = ipv4; pos = portPos = ipv4Pos;}
847  else if (isDNS)
848  {outAddr = dnsName; pos = portPos = dnsPos;}
849 
850  // Check for optional port number
851  char ch (CharAt(portPos));
852  if (ch != ':')
853  return true;
854  ++portPos;
855  if (!ParseDecNumber(portPos, port))
856  {pos = portPos; return false;} // Bad port number!
857  outPort = port;
858  pos = portPos;
859  return true;
860 }
861 
862 bool NTV2DeviceSpecParser::ParseResourcePath (size_t & pos, string & outRsrc)
863 {
864  outRsrc.clear();
865  string rsrc, name;
866  size_t rsrcPos(pos);
867  char ch(CharAt(rsrcPos));
868  while (ch == '/')
869  {
870  ++rsrcPos;
871  rsrc += '/';
872  if (!ParseAlphaNumeric(rsrcPos, name, " "))
873  break;
874  rsrc += name;
875  ch = CharAt(rsrcPos);
876  }
877  if (!rsrc.empty())
878  pos = rsrcPos;
879  outRsrc = rsrc;
880  return !outRsrc.empty();
881 }
882 
883 bool NTV2DeviceSpecParser::ParseParamAssignment (size_t & pos, string & outKey, string & outValue)
884 {
885  outKey.clear(); outValue.clear();
886  string key, value;
887  size_t paramPos(pos);
888  char ch(CharAt(paramPos));
889  if (ch == '&')
890  ch = CharAt(++paramPos);
891  do
892  {
893  if (!ParseAlphaNumeric(paramPos, key, "%"))
894  break;
895  ch = CharAt(paramPos);
896  if (ch != '=')
897  break;
898  ch = CharAt(++paramPos);
899  while (ch != 0 && ch != '&')
900  {
901  value += ch;
902  ch = CharAt(++paramPos);
903  }
904  } while (false);
905  if (!key.empty())
906  {pos = paramPos; outKey = key; outValue = value;}
907  return !key.empty();
908 }
909 
910 bool NTV2DeviceSpecParser::ParseQuery (size_t & pos, NTV2Dictionary & outParams)
911 {
912  outParams.clear();
913  string key, value;
914  size_t queryPos(pos);
915  char ch(CharAt(queryPos));
916  if (ch != '?')
917  return false;
918  queryPos++;
919 
920  while (ParseParamAssignment(queryPos, key, value))
921  {
922  outParams.insert(key, value);
923  ch = CharAt(queryPos);
924  if (ch != '&')
925  break;
926  }
927  if (!outParams.empty())
928  pos = queryPos;
929  return !outParams.empty();
930 }
931 
932 bool NTV2DeviceSpecParser::ParseQueryParams (const NTV2Dictionary & inSrcDict, NTV2Dictionary & outQueryParams) // STATIC
933 {
934  if (!inSrcDict.hasKey(kConnectParamQuery))
935  return true; // It's not an error if there's no 'query' in srcDict
936  string queryStr(inSrcDict.valueForKey(kConnectParamQuery));
937  if (!queryStr.empty())
938  if (queryStr[0] == '?')
939  queryStr.erase(0,1); // Remove leading '?'
940  PLGDBG("Query: '" << queryStr << "'");
941  const NTV2StringList strs(aja::split(queryStr, "&"));
942  for (NTV2StringListConstIter it(strs.begin()); it != strs.end(); ++it)
943  {
944  string str(*it), key, value;
945  if (str.find("=") == string::npos)
946  { // No assignment (i.e. no '=') --- just insert key with empty value...
947  key = ::PercentDecode(aja::lower(str));
948  outQueryParams.insert(key, value);
949  PLGDBG("'" << key << "' = ''");
950  continue;
951  }
952  NTV2StringList pieces(aja::split(str,"="));
953  if (pieces.empty())
954  continue;
955  key = ::PercentDecode(aja::lower(pieces.at(0)));
956  if (pieces.size() > 1)
957  value = pieces.at(1);
958  if (key.empty())
959  {PLGWARN("Empty key '" << key << "'"); continue;}
960  if (outQueryParams.hasKey(key))
961  PLGDBG("Param '" << key << "' value '" << outQueryParams.valueForKey(key) << "' to be replaced with '" << value << "'");
962  outQueryParams.insert(key, ::PercentDecode(value));
963  PLGDBG("'" << key << "' = '" << outQueryParams.valueForKey(key) << "'");
964  } // for each &param
965  return true;
966 } // ParseQueryParams
967 
968 bool NTV2DeviceSpecParser::MakeQueryString (const NTV2Dictionary & inSrcDict, std::string & outQueryStr, const bool inLeadQuestion) // STATIC
969 {
970  outQueryStr.clear();
971  if (inSrcDict.empty())
972  return true; // success, but nothing to do
973  NTV2StringList params;
974  const NTV2StringSet keys (inSrcDict.keys());
975  for (NTV2StringSetConstIter it(keys.begin()); it != keys.end(); ++it)
976  {
977  const string key(*it);
978  if (key.empty())
979  continue; // empty key?!?!
980  ostringstream oss;
981  oss << ::PercentEncode(key);
982  const string val(inSrcDict.valueForKey(key));
983  if (!val.empty())
984  oss << "=" << ::PercentEncode(val);
985  params.push_back(oss.str());
986  } // for each key
987  if (inLeadQuestion)
988  outQueryStr = "?";
989  outQueryStr += aja::join(params, "&");
990  return true;
991 }
992 
993 bool NTV2DeviceSpecParser::IsUpperLetter (const char inChar)
994 { static const string sHexDigits("_ABCDEFGHIJKLMNOPQRSTUVWXYZ");
995  return sHexDigits.find(inChar) != string::npos;
996 }
997 
998 bool NTV2DeviceSpecParser::IsLowerLetter (const char inChar)
999 { static const string sHexDigits("abcdefghijklmnopqrstuvwxyz");
1000  return sHexDigits.find(inChar) != string::npos;
1001 }
1002 
1003 bool NTV2DeviceSpecParser::IsLetter (const char inChar, const bool inIncludeUnderscore)
1004 { return (inIncludeUnderscore && inChar == '_') || IsUpperLetter(inChar) || IsLowerLetter(inChar);
1005 }
1006 
1007 bool NTV2DeviceSpecParser::IsDecimalDigit (const char inChar)
1008 { static const string sDecDigits("0123456789");
1009  return sDecDigits.find(inChar) != string::npos;
1010 }
1011 
1012 bool NTV2DeviceSpecParser::IsHexDigit (const char inChar)
1013 { static const string sHexDigits("0123456789ABCDEFabcdef");
1014  return sHexDigits.find(inChar) != string::npos;
1015 }
1016 
1017 bool NTV2DeviceSpecParser::IsLegalSerialNumChar (const char inChar)
1018 { return IsLetter(inChar) || IsDecimalDigit(inChar);
1019 }
1020 
1021 #if defined(_DEBUG)
1022  void NTV2DeviceSpecParser::test (void)
1023  {
1024  NTV2DeviceSpecParser specParser;
1025  specParser.Reset("1");
1026  specParser.Reset("00000000000000000000000000000000000000000000000000000000000000000000000000000000000001");
1027  specParser.Reset("corvid24");
1028  specParser.Reset("corvid88");
1029  specParser.Reset("konalhi");
1030  specParser.Reset("alpha");
1031  specParser.Reset("00T64450");
1032  specParser.Reset("00t6-450");
1033  specParser.Reset("BLATZBE0");
1034  specParser.Reset("0x424C41545A424530");
1035  specParser.Reset("0x424C415425424530");
1036 
1037  specParser.Reset("badscheme://1");
1038 
1039  specParser.Reset("ntv2local://1");
1040  specParser.Reset("NtV2lOcAl://00000000000000000000000000000000000000000000000000000000000000000000000000000000000001");
1041  specParser.Reset("NTV2Local://corvid24");
1042  specParser.Reset("ntv2local://corvid88");
1043  specParser.Reset("ntv2local://konalhi");
1044  specParser.Reset("ntv2local://alpha");
1045  specParser.Reset("ntv2local://00T64450");
1046  specParser.Reset("ntv2local://00t6-450");
1047  specParser.Reset("ntv2local://BLATZBE0");
1048 
1049  specParser.Reset("ntv2nub://1.2.3.4");
1050  specParser.Reset("ntv2nub://1.2.3.4/doc");
1051  specParser.Reset("ntv2nub://1.2.3.4/doc/");
1052  specParser.Reset("ntv2nub://1.2.3.4/doc/alpha?one&two=2&three=&four=4");
1053  specParser.Reset("ntv2nub://1.2.3.4/doc/?one&two=2&three=&four=4");
1054  specParser.Reset("ntv2nub://1.2.3.4:badport/doc?one&two=2&three=&four=4");
1055  specParser.Reset("ntv2nub://1.2.3.4:200/doc?one&two=2&three=&four=4");
1056  specParser.Reset("ntv2nub://1.2.3.4:200/doc/?one&two=2&three=&four=4");
1057  specParser.Reset("ntv2nub://1.2.3.4:12345");
1058  specParser.Reset("ntv2nub://1.2.3.4:65000/doc");
1059  specParser.Reset("ntv2nub://1.2.3.4:32767/doc/");
1060  specParser.Reset("ntv2nub://1.2.3.4/path/to/doc/");
1061  specParser.Reset("ntv2nub://1.2.3.4/path/to/doc/?");
1062  specParser.Reset("ntv2nub://1.2.3.4/path/to/doc?");
1063  specParser.Reset("ntv2nub://1.2.3.4/path/to/doc/?one");
1064  specParser.Reset("ntv2nub://1.2.3.4/path/to/doc?one");
1065  specParser.Reset("ntv2nub://1.2.3.4/path/to/doc/?one=");
1066  specParser.Reset("ntv2nub://1.2.3.4/path/to/doc?one=");
1067  specParser.Reset("ntv2nub://1.2.3.4/path/to/doc/?one=1");
1068  specParser.Reset("ntv2nub://1.2.3.4/path/to/doc?one=1");
1069  specParser.Reset("ntv2nub://1.2.3.4/path/to/doc/?one=1&two");
1070  specParser.Reset("ntv2nub://1.2.3.4/path/to/doc?one=1&two");
1071  specParser.Reset("ntv2nub://50.200.250.300");
1072  specParser.Reset("ntv2nub://fully.qualified.domain.name.com/path/to/doc/?one=1&two");
1073  specParser.Reset("ntv2nub://fully.qualified.domain.name.edu:badport/path/to/doc/?one=1&two");
1074  specParser.Reset("ntv2nub://fully.qualified.domain.name.info:5544/path/to/doc/?one=1&two");
1075  specParser.Reset("ntv2nub://fully.qualified.domain.name.org/path/to/doc/?one=1&two");
1076  specParser.Reset("ntv2nub://fully.qualified.domain.name.nz:badport/path/to/doc/?one=1&two");
1077  specParser.Reset("ntv2nub://fully.qualified.domain.name.au:000004/path/to/doc/?one=1&two");
1078  specParser.Reset("ntv2nub://fully.qualified.domain.name.ch:4/corvid88");
1079  specParser.Reset("ntv2nub://fully.qualified.domain.name.cn:4/00T64450");
1080  specParser.Reset("ntv2nub://fully.qualified.domain.name.ru:4/2");
1081  specParser.Reset("ntv2nub://fully.qualified.domain.name.co.uk:4/00000000000000000000000000000001");
1082  specParser.Reset("ntv2nub://fully.qualified.domain.name.com:4/0000000000000000000000000000000001");
1083  specParser.Reset("ntv2://swdevice/?"
1084  "nosharedmemory"
1085  "&supportlog=file%3A%2F%2F%2FUsers%2Fdemo%2FDesktop%2FAJAWatcherSupport.log"
1086  "&fbinit=file%3A%2F%2F%2FUsers%2Fdemo%2FDesktop%2FSDRAMsnapshot.dat");
1087  }
1088 #endif // defined(_DEBUG)
1089 
1090 #if defined(MSWindows)
1091  static string WinErrStr (const DWORD inErr)
1092  {
1093  string result("foo");
1094  LPVOID lpMsgBuf;
1095  const DWORD res(FormatMessage ( FORMAT_MESSAGE_ALLOCATE_BUFFER
1096  | FORMAT_MESSAGE_FROM_SYSTEM
1097  | FORMAT_MESSAGE_IGNORE_INSERTS, // dwFlags
1098  AJA_NULL, // lpSource: n/a
1099  inErr, // dwMessageId: n/a
1100  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // dwLanguageId
1101  (LPTSTR) &lpMsgBuf, // output buffer
1102  0, // output buffer size, in TCHARs
1103  AJA_NULL)); // user params
1104  if (lpMsgBuf)
1105  {
1106  if (res)
1107  result = reinterpret_cast<const char *>(lpMsgBuf);
1108  LocalFree(lpMsgBuf);
1109  }
1110  return result;
1111  }
1112 #endif // MSWindows
1113 
1114 
1115 #if !defined(NTV2_PREVENT_PLUGIN_LOAD)
1116 
1117 /*****************************************************************************************************************************************************
1118  NTV2Plugin
1119 *****************************************************************************************************************************************************/
1120 
1123 
1124 // Wraps handle returned from dlopen/LoadLibrary, calls dlclose/FreeLibrary upon destruction
1126 {
1127  public:
1128  static bool LoadPlugin (const string & path, const string & folderPath, NTV2PluginPtr & outPtr, string & outErrMsg, const bool inUseStdout);
1129 
1130  public:
1131  #if defined(MSWindows)
1132  NTV2Plugin (HMODULE handle, const string & path, const bool useStdout);
1133  inline operator HMODULE() const {return mHandle;}
1134  #else
1135  NTV2Plugin (void * handle, const string & path, const bool useStdout);
1136  inline operator void*() const {return mHandle;}
1137  #endif
1138  ~NTV2Plugin (void);
1139  inline bool isLoaded (void) const {return mHandle && !mPath.empty() ? true : false;}
1140  void * addressForSymbol (const string & inSymbol, string & outErrorMsg);
1141 
1142  private:
1143  NTV2Plugin();
1144  NTV2Plugin(const NTV2Plugin & rhs);
1145  NTV2Plugin & operator = (const NTV2Plugin & rhs);
1146  inline bool useStdout(void) {return mUseStdout;}
1147  private:
1148  #if defined(MSWindows)
1149  HMODULE mHandle;
1150  #else
1151  void * mHandle;
1152  #endif
1153  string mPath;
1154  bool mUseStdout;
1155 }; // NTV2Plugin
1156 
1157 // One-stop-shop to load a plugin & instantiate its NTV2Plugin instance
1158 bool NTV2Plugin::LoadPlugin (const string & path, const string & folderPath, NTV2PluginPtr & outPtr, string & outErrMsg, const bool inUseStdout)
1159 {
1160  ostringstream loadErr;
1161  #if defined(AJABareMetal)
1162  return false; // unimplemented
1163  #elif defined(MSWindows)
1164  // Open the DLL (Windows)...
1165  std::wstring dllsFolderW;
1166  aja::string_to_wstring(folderPath, dllsFolderW);
1167  if (!AddDllDirectory(dllsFolderW.c_str()))
1168  {
1169  loadErr << "AddDllDirectory '" << path << "' failed: " << WinErrStr(::GetLastError());
1170  return false;
1171  } // AddDllDirectory failed
1172  HMODULE h = ::LoadLibraryExA(LPCSTR(path.c_str()), AJA_NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
1173  if (!h)
1174  {
1175  const string errStr (::WinErrStr(::GetLastError()));
1176  if (!errStr.empty())
1177  loadErr << errStr;
1178  #if defined(_DEBUG)
1179  if (!errStr.empty())
1180  cerr << "## ERROR: LoadPlugin failed to open '" << path << "':" << endl << errStr << endl;
1181  #endif
1182  }
1183  #else // MacOS or Linux
1184  // Open the .dylib (MacOS) or .so (Linux)...
1185  void * h = ::dlopen(path.c_str(), RTLD_LAZY);
1186  if (!h)
1187  {
1188  const char * pErrorStr(::dlerror());
1189  const string errStr (pErrorStr ? pErrorStr : "");
1190  if (!errStr.empty())
1191  loadErr << errStr;
1192  #if defined(_DEBUG)
1193  if (!errStr.empty())
1194  cerr << "## ERROR: LoadPlugin failed to open '" << path << "':" << endl << loadErr.str() << endl;
1195  #endif
1196  } // dlopen failed
1197  #endif // MacOS or Linux
1198  if (!loadErr.str().empty())
1199  outErrMsg = loadErr.str();
1200  if (!h)
1201  return false;
1202  outPtr = new NTV2Plugin(h, path, inUseStdout);
1203  return outPtr;
1204 }
1205 
1206 NTV2Plugin::NTV2Plugin()
1207 {
1208  NTV2_ASSERT(false);
1210 }
1211 
1213 {
1214  NTV2_ASSERT(false);
1216 }
1217 
1218 NTV2Plugin & NTV2Plugin::operator = (const NTV2Plugin & rhs)
1219 {
1220  NTV2_ASSERT(false);
1221  return *this;
1222 }
1223 
1224 #if defined(MSWindows)
1225 NTV2Plugin::NTV2Plugin (HMODULE handle, const string & path, const bool inUseStdout)
1226 #else
1227 NTV2Plugin::NTV2Plugin (void * handle, const string & path, const bool inUseStdout)
1228 #endif
1229  : mHandle(handle),
1230  mPath(path)
1231 {
1232  NTV2_ASSERT(mHandle);
1233  NTV2_ASSERT(!mPath.empty());
1235  P_NOTE("Dynamic/shared library '" << mPath << "' (" << INSTP(mHandle) << ") loaded, "
1236  << DEC(gPluginConstructCount) << " created, " << DEC(gPluginDestructCount) << " destroyed");
1237 }
1238 
1240 {
1241  if (mHandle)
1242  #if defined(AJABareMetal)
1243  ; // unimplemented
1244  #elif !defined(MSWindows)
1245  ::dlclose(mHandle);
1246  #else // macOS or Linux
1247  ::FreeLibrary(mHandle);
1248  #endif
1250  P_NOTE("Dynamic/shared library '" << mPath << "' (" << INSTP(mHandle) << ") unloaded, "
1251  << DEC(gPluginConstructCount) << " created, " << DEC(gPluginDestructCount) << " destroyed");
1252  mHandle = AJA_NULL;
1253  mPath.clear();
1254 }
1255 
1256 void * NTV2Plugin::addressForSymbol (const string & inSymbolName, string & outErrorMsg)
1257 {
1258  outErrorMsg.clear();
1259  if (!mHandle)
1260  return AJA_NULL;
1261  if (inSymbolName.empty())
1262  return AJA_NULL;
1263  void * result(AJA_NULL);
1264  ostringstream err;
1265  #if defined(AJABareMetal)
1266  // TODO
1267  #elif defined(MSWindows)
1268  result = reinterpret_cast<void*>(::GetProcAddress(reinterpret_cast<HMODULE>(mHandle), inSymbolName.c_str()));
1269  if (!result)
1270  err << "'GetProcAddress' failed for '" << inSymbolName << "': " << WinErrStr(::GetLastError());
1271  #else // MacOS or Linux
1272  result = ::dlsym(mHandle, inSymbolName.c_str());
1273  if (!result)
1274  { const char * pErrorStr(::dlerror());
1275  const string errStr (pErrorStr ? pErrorStr : "");
1276  err << "'dlsym' failed for '" << inSymbolName << "': " << errStr;
1277  }
1278  #endif // MacOS or Linux
1279  outErrorMsg = err.str();
1280  return result;
1281 } // addressForSymbol
1282 
1283 
1284 /*****************************************************************************************************************************************************
1285  @brief A singleton that tracks and monitors loaded plugins, and frees them when no one is using them.
1286  @bug This doesn't work on some platforms when libajantv2 is statically linked into the plugin. When
1287  the plugin loads, the plugin gets its own separate libajantv2 static globals, and thus, a second
1288  set of libajantv2 singletons, including this one.
1289 *****************************************************************************************************************************************************/
1292 
1293 // Singleton that tracks plugin use
1295 {
1296  public: // Class Methods
1297  static PluginRegistry & Get (void);
1298  static void Terminate (void);
1299  static inline void EnableDebugging (const bool inEnable = true) {sDebugRegistry = inEnable;}
1300  static inline bool DebuggingEnabled (void) {return sDebugRegistry;}
1301 
1302  public: // Instance Methods
1303  PluginRegistry();
1304  ~PluginRegistry();
1305  bool loadPlugin (const string & path, const string & folderPath, NTV2PluginPtr & outPtr, string & errMsg, const bool useStdout);
1306  bool unloadPlugin (const string & path, string & errMsg);
1307  bool pluginIsLoaded (const string & path);
1308  bool pluginForPath (const string & path, NTV2PluginPtr & outHandle);
1309  NTV2StringList loadedPlugins (void);
1310  NTV2StringList pluginStats (void); // returns string list, each has pluginPath<tab>refCount
1311  bool hasPath (const string & path);
1312  bool indexForPath (const string & path, size_t & outIndex);
1313  ULWord countForPath (const string & path);
1314  uint32_t * refConForPath (const string & path);
1315  inline bool useStdout (void) const {return DebuggingEnabled();}
1316 
1317  private:
1318  typedef map<string, NTV2PluginPtr> NTV2PluginMap;
1319  NTV2StringList mPluginPaths; // List of unique known plugins; only grows, never shrinks
1320  ULWordSequence mPluginCounts; // Per-plugin instance counts: correlates to gPluginPaths
1321  ULWordSequence mCompareCounts; // For detecting changes to mPluginCounts
1322  NTV2PluginMap mPluginMap; // Maps each unique plugin path to its corresponding NTV2Plugin instance
1323  AJALock mPluginMapLock; // Mutex to serialize access to these registry globals
1324  AJAThread mMonitor; // Thread that monitors plugin utilization
1325  bool mQuitMonitor; // Set true to terminate monitor
1326  private:
1327  static void Monitor (AJAThread * pThread, void * pContext);
1328  void monitor (void); // Monitor thread function
1329  static PluginRegistryPtr sSingleton;
1330  static AJALock sMutex;
1331  static bool sDebugRegistry;
1332 }; // PluginRegistry
1333 
1334 PluginRegistryPtr PluginRegistry::sSingleton;
1335 AJALock PluginRegistry::sMutex;
1336 bool PluginRegistry::sDebugRegistry(false);
1337 
1339 {
1340  AJAAutoLock tmp(&sMutex);
1341  if (!sSingleton)
1342  sSingleton = new PluginRegistry;
1343  return *sSingleton;
1344 }
1345 
1347 {
1348  AJAAutoLock tmp(&sMutex);
1349  PLGWARN("");
1350  sSingleton = PluginRegistryPtr();
1351 }
1352 
1353 void PluginRegistry::Monitor (AJAThread * pThread, void * pContext)
1354 { (void) pThread;
1355  PluginRegistry * pObj (reinterpret_cast<PluginRegistry*>(pContext));
1356  if (pObj)
1357  pObj->monitor();
1358 }
1359 
1361  : mQuitMonitor(false)
1362 {
1363  P_NOTE ("PluginRegistry " << INSTP(this) << " constructed");
1364  mPluginCounts.reserve(256);
1365  for (size_t num(0); num < 256; num++)
1366  mPluginCounts.push_back(0);
1367  mCompareCounts = mPluginCounts;
1368  mMonitor.Attach(Monitor, this);
1370  mMonitor.Start();
1371 }
1372 
1374 {
1375  mQuitMonitor = true;
1376  while (mMonitor.Active())
1377  AJATime::Sleep(10);
1378  P_NOTE("PluginRegistry singleton " << INSTP(this) << " destroyed:" << endl << aja::join(pluginStats(), "\n"));
1379 }
1380 
1381 bool PluginRegistry::loadPlugin (const string & path, const string & folderPath, NTV2PluginPtr & outPtr, string & errMsg, const bool inUseStdout)
1382 {
1383  AJAAutoLock tmp(&mPluginMapLock);
1384  outPtr = NTV2PluginPtr();
1385  if (path.empty())
1386  {P_FAIL("empty path"); return false;}
1387  if (pluginForPath(path, outPtr))
1388  {
1389  NTV2_ASSERT(hasPath(path));
1390  return true;
1391  }
1392  if (hasPath(path))
1393  {P_WARN(INSTP(this) << ": '" << path << "': 'pluginForPath' returned false, but 'hasPath' returned true, count=" << countForPath(path));}
1394  string msg;
1395  if (!NTV2Plugin::LoadPlugin (path, folderPath, outPtr, msg, inUseStdout))
1396  {P_FAIL(msg); return false;}
1397  P_NOTE(INSTP(this) << ": Dynamic/shared library '" << path << "' loaded");
1398  mPluginMap[path] = outPtr;
1399  mPluginPaths.push_back(path);
1400  mPluginCounts.at(mPluginPaths.size()-1) = 0;
1401  return true;
1402 }
1403 
1404 bool PluginRegistry::unloadPlugin (const string & path, string & errMsg)
1405 {
1406  AJAAutoLock tmp(&mPluginMapLock);
1407  NTV2PluginPtr ptr;
1408  if (path.empty())
1409  return false;
1410  if (!pluginForPath(path, ptr))
1411  {P_FAIL(INSTP(this) << ": '" << path << "' requested to unload, but not loaded"); return false;}
1412  mPluginMap.erase(path); // This should cause NTV2Plugin destructor to be called
1413  P_NOTE(INSTP(this) << ": '" << path << "' unloaded");
1414  return true;
1415 }
1416 
1417 bool PluginRegistry::pluginIsLoaded (const string & path)
1418 {
1419  AJAAutoLock tmp(&mPluginMapLock);
1420  return mPluginMap.find(path) != mPluginMap.end();
1421 }
1422 
1423 bool PluginRegistry::pluginForPath (const string & path, NTV2PluginPtr & outHandle)
1424 {
1425  AJAAutoLock tmp(&mPluginMapLock);
1426  NTV2PluginMap::const_iterator it(mPluginMap.find(path));
1427  if (it == mPluginMap.end())
1428  outHandle = NTV2PluginPtr();
1429  else
1430  outHandle = it->second;
1431  return outHandle;
1432 }
1433 
1435 {
1436  NTV2StringList result;
1437  AJAAutoLock tmp(&mPluginMapLock);
1438  for (NTV2PluginMap::const_iterator it(mPluginMap.begin()); it != mPluginMap.end(); ++it)
1439  result.push_back(it->first);
1440  return result;
1441 }
1442 
1444 {
1445  NTV2StringList result;
1446  AJAAutoLock tmp(&mPluginMapLock);
1447  for (size_t ndx(0); ndx < mPluginPaths.size(); ndx++)
1448  {
1449  const string path (mPluginPaths.at(ndx));
1450  ostringstream oss; oss << path << "\t" << DEC(countForPath(path));
1451  NTV2PluginPtr p;
1452  if (pluginForPath(path, p))
1453  oss << "\t" << (p->isLoaded() ? "loaded" : "unloaded");
1454  else
1455  oss << "\t" << "---";
1456  result.push_back(oss.str());
1457  }
1458  return result;
1459 }
1460 
1461 bool PluginRegistry::hasPath (const string & path)
1462 {
1463  size_t ndx(0);
1464  return indexForPath(path,ndx);
1465 }
1466 
1467 bool PluginRegistry::indexForPath (const string & path, size_t & outIndex)
1468 {
1469  AJAAutoLock tmp(&mPluginMapLock);
1470  for (outIndex = 0; outIndex < mPluginPaths.size(); outIndex++)
1471  if (path == mPluginPaths.at(outIndex))
1472  return true;
1473  return false;
1474 }
1475 
1476 uint32_t * PluginRegistry::refConForPath (const string & path)
1477 {
1478  AJAAutoLock tmp(&mPluginMapLock);
1479  size_t ndx(0);
1480  if (indexForPath(path, ndx))
1481  return &mPluginCounts[ndx];
1482  return AJA_NULL;
1483 }
1484 
1486 {
1487  size_t ndx(0);
1488  if (indexForPath(path, ndx))
1489  return mPluginCounts.at(ndx);
1490  return 0;
1491 }
1492 
1493 void PluginRegistry::monitor (void)
1494 {
1495  P_NOTE("PluginRegistry " << INSTP(this) << " monitor started");
1496  ostringstream oss; oss << "PluginReg" << HEX0N(uint32_t(uint64_t(this)),8);
1497  mMonitor.SetThreadName(oss.str().c_str());
1498  while (!mQuitMonitor)
1499  {
1500  {
1501  AJAAutoLock tmp(&mPluginMapLock);
1502  for (size_t ndx(0); ndx < mPluginPaths.size(); ndx++)
1503  {
1504  const uint32_t oldCount(mCompareCounts.at(ndx)), newCount(mPluginCounts.at(ndx));
1505  if (newCount != oldCount)
1506  {
1507  string errMsg, path(mPluginPaths.at(ndx));
1508  if (newCount > oldCount)
1509  {P_NOTE("PluginRegistry " << INSTP(this) << ": Plugin '" << path << "' utilization "
1510  << "increased from " << DEC(oldCount) << " to " << DEC(newCount));}
1511  else
1512  {
1513  P_NOTE("PluginRegistry " << INSTP(this) << ": Plugin '" << path << "' utilization "
1514  << "decreased from " << DEC(oldCount) << " to " << DEC(newCount));
1515  if (newCount == 0)
1516  unloadPlugin(path, errMsg);
1517  } // else count decreased
1518  mCompareCounts.at(ndx) = newCount;
1519  } // something changed
1520  } // for each plugin
1521  }
1522  AJATime::Sleep(250);
1523  }
1524  P_NOTE("PluginRegistry " << INSTP(this) << " monitor stopped");
1525 }
1526 
1527 /*****************************************************************************************************************************************************
1528  @brief Knows how to load & validate a plugin
1529 *****************************************************************************************************************************************************/
1531 {
1532  public: // Instance Methods
1533  NTV2PluginLoader (NTV2Dictionary & params);
1534  ~NTV2PluginLoader ();
1535  void * getFunctionAddress (const string & inFuncName);
1536  inline string pluginPath (void) const {return mDict.valueForKey(kNTV2PluginInfoKey_PluginPath);}
1537  inline string pluginSigPath (void) const {return mDict.valueForKey(kNTV2PluginInfoKey_PluginSigPath);}
1538  inline string pluginsPath (void) const {return mDict.valueForKey(kNTV2PluginInfoKey_PluginsPath);}
1539  inline string pluginBaseName (void) const {return mDict.valueForKey(kNTV2PluginInfoKey_PluginBaseName);}
1540  bool isValidated (void) const;
1541  inline bool showParams (void) const {return mQueryParams.hasKey(kQParamShowParams);}
1542  void * refCon (void) const;
1543 
1544  protected: // Used internally
1545  bool validate (void);
1546  void * getSymbolAddress (const string & inSymbolName, string & outErrorMsg);
1547  bool getPluginsFolder (string & outPath) const;
1548  bool getBaseNameFromScheme (string & outName) const;
1549  inline bool isOpen (void) {return mpPlugin ? mpPlugin->isLoaded() : false;}
1550  inline bool useStdout (void) const {return mQueryParams.hasKey(kQParamLogToStdout);}
1551  inline bool isVerbose (void) const {return mQueryParams.hasKey(kQParamVerboseLogging);}
1552  inline bool showCertificate (void) const {return mQueryParams.hasKey(kQParamShowX509Cert);}
1553  bool fail (void);
1554 
1555  private: // Instance Data
1556  NTV2Dictionary & mDict;
1557  NTV2Dictionary mQueryParams;
1558  NTV2PluginPtr mpPlugin;
1559  bool mValidated;
1560  mutable string errMsg;
1561 
1562  protected: // Class Methods
1563  static bool ExtractCertInfo (NTV2Dictionary & outInfo, const string & inStr);
1564  static bool ExtractIssuerInfo (NTV2Dictionary & outInfo, const string & inStr, const string & inParentKey);
1565  static string mbedErrStr (const int mbedtlsReturnCode);
1566 }; // NTV2PluginLoader
1567 
1568 
1569 // Constructor -- peforms all preparatory work: determines which plugin to load, then loads & validates it
1571  : mDict(params),
1572  mValidated(false)
1573 {
1576  const NTV2Dictionary originalParams(mDict);
1577  if (NTV2DeviceSpecParser::ParseQueryParams (mDict, mQueryParams) && !mQueryParams.empty())
1578  mDict.addFrom(mQueryParams);
1580  mDict.erase(kNTV2PluginInfoKey_Fingerprint); // Be sure caller can't cheat
1581  P_INFO("Loader created for '" << mDict.valueForKey(kConnectParamScheme) << "', " << DEC(gLoaderConstructCount) << " created, "
1582  << DEC(gLoaderDestructCount) << " destroyed");
1583 
1584  // Determine plugin base name & where to find the dylib/dll/so...
1585  string pluginBaseName, pluginsFolder;
1586  if (getBaseNameFromScheme(pluginBaseName) && getPluginsFolder(pluginsFolder))
1587  {
1588  const string path (pluginsFolder + pluginBaseName);
1589  const string sigPath (path + SIG_EXTENSION), dllPath (path + DLL_EXTENSION);
1590  mDict.insert(kNTV2PluginInfoKey_PluginPath, dllPath);
1591  mDict.insert(kNTV2PluginInfoKey_PluginSigPath, sigPath);
1592  if (showParams())
1593  { cout << "## NOTE: Original params for '" << pluginPath() << "':" << endl;
1594  originalParams.Print(cout, false) << endl;
1595  }
1596  // Validate the plugin...
1597  validate();
1598  if (showParams())
1599  { cout << "## NOTE: Final params for '" << pluginPath() << "':" << endl;
1600  mDict.Print(cout, false) << endl;
1601  }
1602  }
1603 }
1604 
1606 {
1608  P_INFO("Loader destroyed for '" << pluginBaseName() << "', " << DEC(gLoaderConstructCount) << " created, "
1609  << DEC(gLoaderDestructCount) << " destroyed");
1610 }
1611 
1612 string NTV2PluginLoader::mbedErrStr (const int mbedtlsReturnCode)
1613 {
1614  NTV2Buffer errBuff(4096);
1615  string str;
1616  mbedtls_strerror (mbedtlsReturnCode, errBuff, errBuff);
1617  errBuff.GetString (str, /*U8Offset*/0, /*maxSize*/errBuff);
1618  return str;
1619 }
1620 
1621 bool NTV2PluginLoader::ExtractCertInfo (NTV2Dictionary & outInfo, const string & inStr)
1622 {
1623  outInfo.clear();
1624  if (inStr.empty())
1625  return false;
1626  string keyPrefix;
1627  NTV2StringList lines(aja::split(inStr, "\n"));
1628  for (size_t lineNdx(0); lineNdx < lines.size(); lineNdx++)
1629  {
1630  string line (lines.at(lineNdx));
1631  const bool indented (line.empty() ? false : line.at(0) == ' ');
1632  aja::strip(line);
1633  if (line.empty()) // there shouldn't be empty lines...
1634  continue; // ...but skip them anyway if they happen to appear
1635  NTV2StringList keyValPair (aja::split(line, " : "));
1636  if (keyValPair.size() != 2)
1637  {
1638  if (keyValPair.size() == 1)
1639  {
1640  keyPrefix = keyValPair.at(0);
1641  aja::replace(keyPrefix, ":", "");
1642  aja::strip(keyPrefix);
1643  continue; // next line
1644  }
1645  PLGFAIL("cert info line " << DEC(lineNdx+1) << " '" << line << "' has "
1646  << DEC(keyValPair.size()) << " column(s) -- expected 2");
1647  return false;
1648  }
1649  string key(keyValPair.at(0)), val(keyValPair.at(1));
1650  if (key.empty())
1651  {PLGFAIL("cert info line " << DEC(lineNdx+1) << " '" << line << "' empty key for value '" << val << "'"); continue;}
1652  if (indented && !keyPrefix.empty())
1653  {aja::strip(key); key = keyPrefix + ": " + key;}
1654  else
1655  {aja::strip(key); keyPrefix.clear();}
1656  if (outInfo.hasKey(key))
1657  val = outInfo.valueForKey(key) + ", " + val;
1658  outInfo.insert(key, aja::strip(val)); // ignore errors for now
1659  } // for each info line
1660  return true;
1661 } // ExtractCertInfo
1662 
1663 bool NTV2PluginLoader::ExtractIssuerInfo (NTV2Dictionary & outInfo, const string & inStr, const string & inParentKey)
1664 {
1665  outInfo.clear();
1666  if (inStr.empty())
1667  return false;
1668  string str(inStr);
1669  NTV2StringList pairs(aja::split(aja::replace(str, "\\,", ","), ", ")), normalized;
1670  string lastKey;
1671  for (size_t ndx(0); ndx < pairs.size(); ndx++)
1672  {
1673  string assignment (pairs.at(ndx));
1674  if (assignment.find('=') == string::npos)
1675  {
1676  if (!lastKey.empty())
1677  outInfo.insert (lastKey, outInfo.valueForKey(lastKey) + ", " + assignment);
1678  }
1679  else
1680  {
1681  NTV2StringList pieces (aja::split(assignment, "="));
1682  if (pieces.size() != 2)
1683  {PLGFAIL("'" << inParentKey << "' assignment '" << assignment << "' has " << pieces.size() << " component(s) -- expected 2"); continue;}
1684  lastKey = pieces.at(0);
1685  string val(pieces.at(1));
1686  outInfo.insert (aja::strip(lastKey), val);
1687  }
1688  } // for each key/val assignment
1689  return true;
1690 } // ExtractIssuerInfo
1691 
1692 void * NTV2PluginLoader::getSymbolAddress (const string & inSymbolName, string & outErrorMsg)
1693 {
1694  outErrorMsg.clear();
1695  if (!mpPlugin)
1696  return AJA_NULL;
1697  return mpPlugin->addressForSymbol(inSymbolName, outErrorMsg);
1698 } // getSymbolAddress
1699 
1700 void * NTV2PluginLoader::refCon (void) const
1701 {
1703 }
1704 
1705 bool NTV2PluginLoader::getPluginsFolder (string & outPath) const
1706 {
1707  if (!pluginsPath().empty())
1708  {outPath = pluginsPath(); return true;} // already known, assumed to be good
1709 
1710  // Plugins are expected to be in the "aja" folder (the parent folder of the "aja/firmware" folder)...
1711  outPath = ::NTV2GetPluginsFolderPath(true/*include trailing slash*/);
1712  if (outPath.empty())
1713  return false;
1714  PLGDBG("AJA plugin path is '" << outPath << "'");
1715  mDict.insert(kNTV2PluginInfoKey_PluginsPath, outPath); // Store it in 'PluginsPath'
1716  return !outPath.empty(); // Success if not empty
1717 }
1718 
1719 bool NTV2PluginLoader::getBaseNameFromScheme (string & outName) const
1720 {
1721  if (!pluginBaseName().empty())
1722  {outName = pluginBaseName(); return true;} // already known, assumed to be good
1723 
1724  // URL scheme determines plugin base name...
1725  if (!mDict.hasKey(kConnectParamScheme))
1726  {P_FAIL("Missing scheme -- params: " << mDict); return false;} // No scheme
1727  string scheme(mDict.valueForKey(kConnectParamScheme));
1728  outName = scheme;
1729  mDict.insert(kNTV2PluginInfoKey_PluginBaseName, outName);
1730  return !outName.empty(); // Success if not empty
1731 }
1732 
1734 {
1735  if (mDict.hasKey(kNTV2PluginInfoKey_Errors))
1736  { const string v(mDict.valueForKey(kNTV2PluginInfoKey_Errors) + "\n" + errMsg);
1739  }
1740  else
1741  mDict.insert(kNTV2PluginInfoKey_Errors, errMsg);
1742  return false;
1743 }
1744 
1746 {
1747  // Load contents of plugin & sig files into sigContent & dllContent buffers...
1748  NTV2Buffer sigContent, dllContent;
1749  {
1750  // no more than 500MB
1751  const size_t maxBufSize = 512*1024*1024;
1752 
1753  ifstream dllF;
1754  dllF.open(pluginPath(), std::ios::in | std::ios::binary);
1755  if (dllF.fail())
1756  {P_FAIL("Could not open plugin file '" << pluginPath() << "'"); return fail();}
1757  if (!dllF.seekg(0, ios_base::end))
1758  {P_FAIL("Could not seek to end of plugin file '" << pluginPath() << "'"); return fail();}
1759  ifstream::pos_type curOffset(dllF.tellg());
1760  if (int(curOffset) == -1)
1761  {P_FAIL("Could not determine size of plugin file '" << pluginPath() << "'"); return fail();}
1762  size_t size = size_t(curOffset);
1763  if (size == 0)
1764  {P_FAIL("Plugin file '" << pluginPath() << "' is empty"); return fail();}
1765  if (size > maxBufSize)
1766  {P_FAIL("EOF not reached in plugin file '" << pluginPath() << "' -- over 500MB in size?"); return fail();}
1767  size += 1;
1768 
1769  NTV2Buffer tmp(size);
1770  if (!dllF.seekg(0, ios_base::beg))
1771  {P_FAIL("Could not seek back to start of plugin file '" << pluginPath() << "'");return fail();}
1772  if (!dllF.read(tmp, tmp.GetByteCount()).eof())
1773  {P_FAIL("EOF not reached in plugin file '" << pluginPath() << "' -- over 500MB in size?"); return fail();}
1774  tmp.Truncate(size_t(dllF.gcount()));
1775  dllContent = tmp;
1776 
1777  tmp.Allocate(size);
1778  ifstream sigF(pluginSigPath().c_str(), std::ios::in | std::ios::binary);
1779  if (!sigF.good())
1780  {P_FAIL("Signature file '" << pluginSigPath() << "' missing"); return fail();}
1781  if (!sigF.read(tmp, tmp.GetByteCount()).eof())
1782  {P_FAIL("EOF not reached in signature file '" << pluginSigPath() << "' -- over 500MB in size?"); return fail();}
1783  tmp.Truncate(size_t(sigF.gcount()));
1784  sigContent = tmp;
1785  }
1786 
1787  // Decode sigContent...
1788  NTV2Dictionary dict;
1789  { const string dictStr (reinterpret_cast<const char*>(sigContent.GetHostPointer()), size_t(sigContent.GetByteCount()));
1790  if (!dict.deserialize(dictStr))
1791  {P_FAIL("Unable to decode signature file '" << pluginSigPath() << "'"); return fail();}
1792  }
1793  P_DBG(DEC(dict.keys().size()) << " keys found in signature file '" << pluginSigPath() << "': " << dict.keys());
1794  NTV2Buffer checksumFromSigFile, x509CertFromSigFile, signature;
1796  {P_FAIL("Signature file '" << pluginSigPath() << "' missing '" << kNTV2PluginSigFileKey_X509Certificate << "' key"); return fail();}
1798  {P_FAIL("Signature file '" << pluginSigPath() << "' missing '" << kNTV2PluginSigFileKey_Signature << "' key"); return fail();}
1799  if (!x509CertFromSigFile.SetFromHexString(dict.valueForKey(kNTV2PluginSigFileKey_X509Certificate)))
1800  {P_FAIL("'SetFromHexString' failed to decode X509 certificate extracted from '" << pluginSigPath() << "' key '" << kNTV2PluginSigFileKey_X509Certificate << "'"); return fail();}
1801  if (!signature.SetFromHexString(dict.valueForKey(kNTV2PluginSigFileKey_Signature)))
1802  {P_FAIL("'SetFromHexString' failed to decode signature extracted from '" << pluginSigPath() << "' key '" << kNTV2PluginSigFileKey_Signature << "'"); return fail();}
1803  if (isVerbose()) {string s; if (signature.toHexString(s)) cout << "## DEBUG1: signature: " << s << endl;}
1804 
1805  // Grab the signing certificate found in the .sig file...
1806  mbedtls_x509_crt crt; // Container for X509 certificate
1807  mbedtls_x509_crt_init(&crt); // Initialize it as empty
1808  int ret = mbedtls_x509_crt_parse(&crt, x509CertFromSigFile, x509CertFromSigFile);
1809  if (ret)
1810  { P_FAIL("'mbedtls_x509_crt_parse' returned " << ret << " (" << mbedErrStr(ret) << ") for X509 cert found in '" << pluginSigPath() << "'");
1811  mbedtls_x509_crt_free(&crt);
1812  return fail();
1813  }
1814 
1815  // Extract certificate info...
1816  NTV2Dictionary certInfo, issuerInfo, subjectInfo;
1817  {
1818  NTV2Buffer msgBuff(4096);
1819  int msgLength (mbedtls_x509_crt_info (msgBuff, msgBuff, /*prefixString*/"", &crt));
1820  string msg (msgBuff, size_t(msgLength));
1821  if (msg.empty())
1822  { P_FAIL("'mbedtls_x509_crt_info' returned no info for X509 cert found in '" << pluginSigPath() << "'");
1823  mbedtls_x509_crt_free(&crt);
1824  return fail();
1825  }
1826  if (showCertificate())
1827  cout << "## DEBUG: Raw X509 certificate info extracted from signature file '" << pluginSigPath() << "':" << endl
1828  << " " << msg << endl;
1829  if (!ExtractCertInfo (certInfo, msg))
1830  { mbedtls_x509_crt_free(&crt);
1831  return false;
1832  }
1833  if (isVerbose())
1834  { cout << "## NOTE: X509 certificate info extracted from signature file '" << pluginSigPath() << "':" << endl;
1835  certInfo.Print(cout, false) << endl;
1836  }
1837  if (certInfo.hasKey("issuer name"))
1838  if (!ExtractIssuerInfo (issuerInfo, certInfo.valueForKey("issuer name"), "issuer name"))
1839  { mbedtls_x509_crt_free(&crt);
1840  return false;
1841  }
1842  if (certInfo.hasKey("subject name"))
1843  if (!ExtractIssuerInfo (subjectInfo, certInfo.valueForKey("subject name"), "subject name"))
1844  { mbedtls_x509_crt_free(&crt);
1845  return false;
1846  }
1847  if (!certInfo.hasKey(kNTV2PluginInfoKey_Fingerprint))
1848  { P_FAIL("Missing key '" << kNTV2PluginInfoKey_Fingerprint << "' in X509 certificate from '" << pluginSigPath() << "'");
1849  mbedtls_x509_crt_free(&crt);
1850  return fail();
1851  }
1852  if (isVerbose() && !issuerInfo.empty())
1853  { cout << "## NOTE: 'issuer name' info:" << endl;
1854  issuerInfo.Print(cout, false) << endl;
1855  }
1856  if (isVerbose() && !subjectInfo.empty())
1857  { cout << "## NOTE: 'subject name' info:" << endl;
1858  subjectInfo.Print(cout, false) << endl;
1859  }
1860  if (!issuerInfo.hasKey(kNTV2PluginX500AttrKey_CommonName))
1861  { P_FAIL("Missing 'Issuer' key '" << kNTV2PluginX500AttrKey_CommonName << "' in X509 certificate from '" << pluginSigPath() << "'");
1862  mbedtls_x509_crt_free(&crt);
1863  return fail();
1864  }
1866  { P_FAIL("Missing 'Issuer' key '" << kNTV2PluginX500AttrKey_OrganizationName << "' in X509 certificate from '" << pluginSigPath() << "'");
1867  mbedtls_x509_crt_free(&crt);
1868  return fail();
1869  }
1871  { P_FAIL("Missing 'Subject' key '" << kNTV2PluginX500AttrKey_OrganizationalUnitName << "' in X509 certificate from '" << pluginSigPath() << "'");
1872  mbedtls_x509_crt_free(&crt);
1873  return fail();
1874  }
1875  mDict.addFrom(certInfo); // Store certInfo key/value pairs into client/server instance's params...
1876  }
1877 
1878  // Compute SHA256 hash of plugin...
1879  NTV2Buffer checksumFromDLL(32);
1880  ret = mbedtls_md_file (mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), pluginPath().c_str(), checksumFromDLL);
1881  if (ret)
1882  { P_FAIL("'mbedtls_md_file' returned " << ret << " (" << mbedErrStr(ret) << ") for '" << pluginPath() << "'");
1883  mbedtls_x509_crt_free(&crt);
1884  return fail();
1885  }
1886  if (isVerbose()) {string s; if (checksumFromDLL.toHexString(s)) cout << "## DEBUG: checksumFromDLL: " << s << endl;}
1887  if (isVerbose()) {string s; if (signature.toHexString(s)) cout << "## DEBUG2: signature: " << s << endl;}
1888 
1889  // Verify the dylib/DLL/so signature...
1890  ret = mbedtls_pk_verify (&crt.pk, // PK context
1891  MBEDTLS_MD_SHA256, // msg digest type (hash algorithm used)
1892  checksumFromDLL, // Ptr to hash of signed msg
1893  0, // Byte length of hash --- zero means "use length associated with msg digest type instead"
1894  signature, // Ptr to signature being verified
1895  signature.GetByteCount()); // Length of signature being verified (in bytes)
1896  if (ret)
1897  { P_FAIL("'mbedtls_pk_verify' returned " << ret << " (" << mbedErrStr(ret) << ") for '" << pluginSigPath() << "'");
1898  mbedtls_x509_crt_free(&crt);
1899  return fail();
1900  }
1901  mbedtls_x509_crt_free(&crt); // Done using the mbedtls_x509_crt struct
1902  P_DBG("'mbedtls_pk_verify' succeeded for '" << pluginPath() << "' -- signature valid");
1903 
1904  // Load/open the shared library...
1905  if (!mpPlugin)
1906  if (!PluginRegistry::Get().loadPlugin (pluginPath(), pluginsPath(), mpPlugin, errMsg, useStdout()))
1907  return fail();
1908  PLGDBG("'" << pluginPath() << "' opened");
1909 
1910  // Obtain AJA Registration Info...
1911  NTV2Dictionary regInfo;
1912  string errGetInfo;
1913  void * pGetInfo = getSymbolAddress(kFuncNameGetRegInfo, errGetInfo);
1914  if (!pGetInfo)
1915  { P_FAIL("'" << pluginPath() << "': '" kFuncNameGetRegInfo "' failed: " << errGetInfo);
1916  return fail();
1917  }
1918  fpGetRegistrationInfo pGetRegInfo = reinterpret_cast<fpGetRegistrationInfo>(pGetInfo);
1919  if (!(*pGetRegInfo)(uint32_t(AJA_NTV2_SDK_VERSION), regInfo))
1920  {P_FAIL("'" << pluginPath() << "': '" << kFuncNameGetRegInfo << "' failed"); return fail();}
1921  PLGDBG("'" << pluginPath() << "': '" << kFuncNameGetRegInfo << "': returned " << regInfo.keys());
1922  if (regInfo.empty())
1923  {P_FAIL("'" << pluginPath() << "': no registration info (empty)"); return fail();}
1924 
1925  // Check for required registration info keys...
1926  NTV2StringList missingRegInfoKeys;
1931  for (size_t ndx(0); ndx < reqKeys.size(); ndx++)
1932  if (!regInfo.hasKey(reqKeys.at(ndx)))
1933  missingRegInfoKeys.push_back(reqKeys.at(ndx));
1934  if (!missingRegInfoKeys.empty())
1935  { P_FAIL("'" << pluginPath() << "': missing key(s) in registration info: '"
1936  << aja::join(missingRegInfoKeys, "','") << "'");
1937  return fail(); // fail
1938  }
1939  mDict.addFrom(regInfo); // Add registration info to plugin's dictionary
1940 
1941  // Check planet alignment...
1942  const string cnReg(regInfo.valueForKey(kNTV2PluginRegInfoKey_CommonName)),
1943  cnCert(issuerInfo.valueForKey(kNTV2PluginX500AttrKey_CommonName));
1944  const string onReg(regInfo.valueForKey(kNTV2PluginRegInfoKey_Vendor)),
1946  const string ouReg(regInfo.valueForKey(kNTV2PluginRegInfoKey_OrgUnit)),
1948  const string myVers(NTV2RPCBase::ShortSDKVersion()),
1950  const string ajaFingerprint(NTV2RPCBase::AJAFingerprint (/*lowerCase*/true, /*stripColons*/false));
1951  string fingerprint(mDict.valueForKey(kNTV2PluginInfoKey_Fingerprint));
1952  aja::lower(fingerprint); // since ajaFingerprint is lower case
1953  if (onReg != onCert)
1954  { P_FAIL("Vendor name (key='" << kNTV2PluginRegInfoKey_Vendor << "') \"" << onReg << "\" from plugin \""
1955  << pluginPath() << "\" doesn't match organization name (key='" << kNTV2PluginX500AttrKey_OrganizationName
1956  << "') \"" << onCert << "\" from X509 certificate 'Issuer' in '" << pluginSigPath() << "'");
1957  return fail();
1958  }
1959  if (cnReg != cnCert)
1960  { P_FAIL("Common name (key='" << kNTV2PluginRegInfoKey_CommonName << "') \"" << cnReg << "\" from plugin \""
1961  << pluginPath() << "\" doesn't match common name (key='" << kNTV2PluginX500AttrKey_CommonName
1962  << "') \"" << cnCert << "\" from X509 certificate 'Issuer' in '" << pluginSigPath() << "'");
1963  return fail();
1964  }
1965  if (ouReg != ouCert)
1966  { P_FAIL("Org unit (key='" << kNTV2PluginX500AttrKey_OrganizationalUnitName << "') \"" << ouReg << "\" from plugin \""
1967  << pluginPath() << "\" doesn't match org unit (key='" << kNTV2PluginX500AttrKey_OrganizationalUnitName
1968  << "') \"" << ouCert << "\" from X509 certificate 'Subject' in '" << pluginSigPath() << "'");
1969  return fail();
1970  }
1971  if (myVers != plVers)
1972  { P_FAIL("SDK version '" << plVers << "' from plugin \"" << pluginPath()
1973  << "\" doesn't match client SDK version '" << myVers << "'");
1974  return fail();
1975  }
1976 #if 1
1977  if (fingerprint != ajaFingerprint)
1978  { P_FAIL("'" << pluginPath() << "':|Plugin not authorized/signed by AJA:|"
1979  << "Issuer serial: " << fingerprint << "|AJA serial: " << ajaFingerprint);
1980  return fail(); // fail
1981  }
1982 #endif
1983  // Green light
1984  mValidated = true;
1985  return true;
1986 } // validate
1987 
1988 // Returns address of function having given name
1989 void * NTV2PluginLoader::getFunctionAddress (const string & inFuncName)
1990 {
1991  // Load/open the shared library...
1992  if (!isOpen())
1993  {P_FAIL("'" << inFuncName << "': '" << pluginPath() << "' not loaded"); return AJA_NULL;}
1994  if (!isValidated())
1995  {P_FAIL("'" << inFuncName << "': '" << pluginPath() << "' not validated"); return AJA_NULL;}
1996 
1997  // Finally, the last step ---- get address of requested function...
1998  string errStr;
1999  void * pResult = getSymbolAddress(inFuncName, errStr);
2000  if (!pResult)
2001  {P_FAIL("'" << inFuncName << "': '" << pluginPath() << "': " << errStr); return AJA_NULL;}
2002  P_DBG("Calling '" << inFuncName << "' in '" << pluginPath() << "'");
2003  return pResult;
2004 } // getFunctionAddress
2005 
2007 {
2008  return mpPlugin && mValidated;
2009 }
2010 
2012 {
2013  const NTV2StringList paths (PluginRegistry::Get().loadedPlugins());
2014  if (paths.empty())
2015  cout << "0 plugins" << endl;
2016  else if (paths.size() == 1)
2017  cout << "1 plugin: " << paths.at(0) << endl;
2018  else cout << DEC(paths.size()) << " plugins:" << endl << aja::join(paths, "\n") << endl;
2019 }
2020 #endif // !defined(NTV2_PREVENT_PLUGIN_LOAD)
2021 
2022 
2023 /*****************************************************************************************************************************************************
2024  NTV2RPCBase
2025 *****************************************************************************************************************************************************/
2026 
2028  : mParams(params),
2029  mpRefCon(pRefCon)
2030 {
2031  NTV2Dictionary queryParams;
2033  if (mpRefCon)
2035  PDBGX("refCnt=" << DEC(mpRefCon ? *mpRefCon : 0) << ", " << DEC(gBaseConstructCount) << " created, "
2036  << DEC(gBaseDestructCount) << " destroyed");
2038  {cout << __FILE__ << "(" << __LINE__ << "):" << AJAFUNC << ":" << endl; mParams.Print(cout, false) << endl;}
2039 }
2040 
2042 {
2044  if (mpRefCon)
2046  PDBGX("refCnt=" << DEC(mpRefCon ? *mpRefCon : 0) << ", " << DEC(gBaseConstructCount) << " created, "
2047  << DEC(gBaseDestructCount) << " destroyed");
2048 }
2049 
2050 bool NTV2RPCBase::SetParams (const NTV2ConnectParams & inNewParams, const bool inAugment)
2051 {
2052  AJAAutoLock tmp(&mParamLock);
2053  size_t oldCount(mParams.size()), updated(0), added(0);
2054  if (inAugment)
2055  {
2056  updated = mParams.updateFrom(inNewParams);
2057  added = mParams.addFrom(inNewParams);
2058  NBSDBG(DEC(updated) << " param(s) updated, " << DEC(added) << " added: " << mParams);
2059  }
2060  else
2061  {
2062  mParams = inNewParams;
2063  NBSDBG(DEC(oldCount) << " param(s) removed, replaced with " << inNewParams);
2064  }
2065  if (mParams.empty())
2066  NBSWARN("No params");
2067  return true;
2068 }
2069 
2071 {
2072  string result(::NTV2Version());
2073  const NTV2StringList halves(aja::split(result, " "));
2074  if (halves.empty())
2075  return result;
2076  NTV2StringList nums(aja::split(halves.front(), "."));
2077  while (nums.size() > 3)
2078  nums.pop_back();
2079  return aja::join(nums, ".");
2080 }
2081 
2082 
2083 string NTV2RPCBase::AJAFingerprint (const bool inLowerCase, const bool inStripColons)
2084 {
2085  static const string sAJAFingerprint ("70:1A:37:93:FA:4F:34:30:58:55:51:0C:01:4E:45:7C:BE:5B:41:65");
2086  string result(sAJAFingerprint);
2087  if (inStripColons)
2088  aja::replace(result, ":", "");
2089  if (inLowerCase)
2090  aja::lower(result);
2091  return result;
2092 }
2093 
2094 
2095 /*****************************************************************************************************************************************************
2096  NTV2RPCClientAPI
2097 *****************************************************************************************************************************************************/
2098 
2100  : NTV2RPCBase(inParams, reinterpret_cast<ULWord*>(pRefCon))
2101 {
2102  AJADebug::Open();
2104  PDBGX(DEC(gClientConstructCount) << " created, " << DEC(gClientDestructCount) << " destroyed");
2105 }
2106 
2108 {
2109  if (IsConnected())
2110  NTV2Disconnect();
2112  PDBGX(DEC(gClientConstructCount) << " created, " << DEC(gClientDestructCount) << " destroyed");
2113 }
2114 
2116 {
2117  AJAAutoLock tmp(&mParamLock);
2118  return mParams;
2119 }
2120 
2121 bool NTV2RPCClientAPI::HasConnectParam (const string & inParam) const
2122 {
2123  AJAAutoLock tmp(&mParamLock);
2124  return mParams.hasKey(inParam);
2125 }
2126 
2127 string NTV2RPCClientAPI::ConnectParam (const string & inParam) const
2128 {
2129  AJAAutoLock tmp(&mParamLock);
2130  return mParams.valueForKey(inParam);
2131 }
2132 
2134 {
2136 }
2137 
2138 ostream & NTV2RPCClientAPI::Print (ostream & oss) const
2139 {
2140  oss << (IsConnected() ? "Connected" : "Disconnected");
2141  if (IsConnected() && !Name().empty())
2142  oss << " to '" << Name() << "'";
2143  return oss;
2144 }
2145 
2147 {
2148  NTV2StringList strs;
2149  string fName(ConnectParam(kQParamVDevFileName)), hostName;
2150  if (!fName.empty())
2151  strs.push_back(string("from '") + fName + "'");
2153  sysInfo.GetValue(AJA_SystemInfoTag_System_Name, hostName);
2154  if (!hostName.empty())
2155  {strs.push_back("on"); strs.push_back(string("'") + hostName + "'");}
2156  return aja::join(strs, " ");
2157 }
2158 
2160 {
2161  if (IsConnected())
2162  NTV2Disconnect();
2163  return NTV2OpenRemote();
2164 }
2165 
2167 {
2168  return NTV2CloseRemote();
2169 }
2170 
2171 bool NTV2RPCClientAPI::NTV2ReadRegisterRemote (const ULWord regNum, ULWord & outRegValue, const ULWord regMask, const ULWord regShift)
2172 { (void) regNum; (void) outRegValue; (void) regMask; (void) regShift;
2173  return false; // UNIMPLEMENTED
2174 }
2175 
2176 bool NTV2RPCClientAPI::NTV2WriteRegisterRemote (const ULWord regNum, const ULWord regValue, const ULWord regMask, const ULWord regShift)
2177 { (void) regNum; (void) regValue; (void) regMask; (void) regShift;
2178  return false; // UNIMPLEMENTED
2179 }
2180 
2182 { (void) autoCircData;
2183  return false; // UNIMPLEMENTED
2184 }
2185 
2187 { (void) eInterrupt; (void) timeOutMs;
2188  return false; // UNIMPLEMENTED
2189 }
2190 
2191 #if !defined(NTV2_DEPRECATE_16_3)
2193  { (void) bitFileType;
2194  ::memset(&bitFileInfo, 0, sizeof(bitFileInfo));
2195  return false; // UNIMPLEMENTED
2196  }
2197 
2199  {
2200  ::memset(&buildInfo, 0, sizeof(buildInfo));
2201  return false; // UNIMPLEMENTED
2202  }
2203 
2205  const UWord signalMask, const bool testPatDMAEnb, const ULWord testPatNum)
2206  { (void) channel; (void) testPatternFBF; (void) signalMask; (void) testPatDMAEnb; (void) testPatNum;
2207  return false; // UNIMPLEMENTED
2208  }
2209 
2210  bool NTV2RPCClientAPI::NTV2ReadRegisterMultiRemote (const ULWord numRegs, ULWord & outFailedRegNum, NTV2RegInfo outRegs[])
2211  { (void) numRegs; (void) outFailedRegNum; (void) outRegs;
2212  return false; // UNIMPLEMENTED
2213  }
2214 
2216  {
2217  outDriverVersion = 0xFFFFFFFF;
2218  return false; // UNIMPLEMENTED
2219  }
2220 #endif // !defined(NTV2_DEPRECATE_16_3)
2221 
2222 bool NTV2RPCClientAPI::NTV2DMATransferRemote ( const NTV2DMAEngine inDMAEngine, const bool inIsRead, const ULWord inFrameNumber,
2223  NTV2Buffer & inOutFrameBuffer, const ULWord inCardOffsetBytes,
2224  const ULWord inNumSegments, const ULWord inSegmentHostPitch,
2225  const ULWord inSegmentCardPitch, const bool inSynchronous)
2226 { (void) inDMAEngine; (void) inIsRead; (void) inFrameNumber; (void) inOutFrameBuffer;
2227  (void) inCardOffsetBytes; (void) inNumSegments; (void) inSegmentHostPitch;
2228  (void) inSegmentCardPitch; (void) inSynchronous;
2229  return false; // UNIMPLEMENTED
2230 }
2231 
2233 { (void) pInMessage;
2234  return false; // UNIMPLEMENTED
2235 }
2236 
2237 bool NTV2RPCClientAPI::NTV2GetBoolParamRemote (const ULWord inParamID, ULWord & outValue)
2238 { (void) inParamID;
2239  outValue = 0;
2240  return false; // UNIMPLEMENTED
2241 }
2242 
2244 { (void) inParamID;
2245  outValue = 0;
2246  return false; // UNIMPLEMENTED
2247 }
2248 
2249 bool NTV2RPCClientAPI::NTV2GetSupportedRemote (const ULWord inEnumsID, ULWordSet & outSupported)
2250 { (void) inEnumsID;
2251  outSupported.clear();
2252  return false; // UNIMPLEMENTED
2253 }
2254 
2256 {
2257  return false; // UNIMPLEMENTED
2258 }
2259 
2261 {
2262 // AJAAutoLock tmp(&mParamLock);
2263 // mParams.clear();
2264  return true;
2265 }
2266 
2268 {
2269 #if defined(NTV2_PREVENT_PLUGIN_LOAD)
2270  return AJA_NULL;
2271 #else
2272  NTV2RPCClientAPI * pRPCObject(AJA_NULL);
2273  {
2274  NTV2PluginLoader loader(params);
2275  fpCreateClient pFunc (reinterpret_cast<fpCreateClient>(loader.getFunctionAddress(kFuncNameCreateClient)));
2276  if (!pFunc)
2277  {NBCFAIL("'" << kFuncNameCreateClient << "' has NULL function address: " << params); return AJA_NULL;}
2278 
2279  // Call plugin's Create function to instantiate the NTV2RPCClientAPI object...
2280  try {
2281  pRPCObject = (*pFunc) (loader.refCon(), params, AJA_NTV2_SDK_VERSION);
2282  } catch (std::bad_alloc &) {
2283  pRPCObject = nullptr;
2284  NBCFAIL("'" << kFuncNameCreateClient << "' bad_alloc exception in CreateClient function");
2285  } catch (...) {
2286  pRPCObject = nullptr;
2287  NBCFAIL("'" << kFuncNameCreateClient << "' exception in CreateClient function");
2288  }
2289  if (!pRPCObject)
2290  NBCFAIL("'" << kFuncNameCreateClient << "' returned NULL client instance from: " << params);
2291  else
2292  NBCINFO("'" << kFuncNameCreateClient << "' created client instance " << xHEX0N(uint64_t(pRPCObject),16));
2293  } // loader freed here
2294  return pRPCObject;
2295 #endif
2296 } // CreateClient
2297 
2298 
2299 /*****************************************************************************************************************************************************
2300  NTV2RPCServerAPI
2301 *****************************************************************************************************************************************************/
2302 
2304 {
2305 #if defined(NTV2_PREVENT_PLUGIN_LOAD)
2306  return AJA_NULL;
2307 #else
2308  NTV2RPCServerAPI * pRPCObject(AJA_NULL);
2309  {
2310  NTV2PluginLoader loader(params);
2311  fpCreateServer pFunc = reinterpret_cast<fpCreateServer>(loader.getFunctionAddress(kFuncNameCreateServer));
2312  if (!pFunc)
2313  {NBCFAIL("'" << kFuncNameCreateServer << "' has NULL function address: " << params); return AJA_NULL;}
2314 
2315  // Call plugin's Create function to instantiate the NTV2RPCServerAPI object...
2316  try {
2317  pRPCObject = (*pFunc) (loader.refCon(), params, AJA_NTV2_SDK_VERSION);
2318  } catch (std::bad_alloc &) {
2319  pRPCObject = nullptr;
2320  NBCFAIL("'" << kFuncNameCreateServer << "' bad_alloc exception in CreateServer function");
2321  } catch (...) {
2322  pRPCObject = nullptr;
2323  NBCFAIL("'" << kFuncNameCreateServer << "' exception in CreateServer function");
2324  }
2325  if (!pRPCObject)
2326  NBCFAIL("'" << kFuncNameCreateServer << "' returned NULL server instance from: " << params);
2327  else
2328  NBCINFO("'" << kFuncNameCreateServer << "' created server instance " << xHEX0N(uint64_t(pRPCObject),16));
2329  }
2330  return pRPCObject; // It's caller's responsibility to delete pRPCObject
2331 #endif
2332 } // CreateServer
2333 
2334 NTV2RPCServerAPI * NTV2RPCServerAPI::CreateServer (const string & inURL) // CLASS METHOD
2335 {
2336  NTV2DeviceSpecParser parser(inURL);
2337  if (parser.HasErrors())
2338  {
2339  NBSFAIL(parser.Error() << " in URL:\n" << inURL);
2340  parser.PrintErrors(cerr);
2341  return AJA_NULL;
2342  }
2343  NTV2ConfigParams parms(parser.Results());
2344  return CreateServer(parms);
2345 }
2346 
2348  : NTV2RPCBase(inParams, reinterpret_cast<ULWord*>(pRefCon))
2349 {
2350  mRunning = mTerminate = false;
2351  NTV2Buffer spare(&mSpare, sizeof(mSpare)); spare.Fill(0ULL);
2352  AJADebug::Open();
2354  PDBGX(DEC(gServerConstructCount) << " created, " << DEC(gServerDestructCount) << " destroyed");
2355 }
2356 
2358 {
2359  Stop();
2360  while (IsRunning())
2361  AJATime::Sleep(50);
2363  PDBGX(DEC(gServerConstructCount) << " created, " << DEC(gServerDestructCount) << " destroyed");
2364 }
2365 
2367 { // This function normally should never be called;
2368  // It's usually overridden by a subclass
2369  NBSDBG("Started");
2370  while (!mTerminate)
2371  AJATime::Sleep(500);
2372  NBSDBG("Terminated");
2373 } // ServerFunction
2374 
2375 ostream & NTV2RPCServerAPI::Print (ostream & oss) const
2376 {
2377  oss << mParams;
2378  return oss;
2379 }
2380 
2382 {
2383  AJAAutoLock tmp(&mParamLock);
2384  return mParams;
2385 }
2386 
2387 bool NTV2RPCServerAPI::HasConfigParam (const string & inParam) const
2388 {
2389  AJAAutoLock tmp(&mParamLock);
2390  return mParams.hasKey(inParam);
2391 }
2392 
2393 string NTV2RPCServerAPI::ConfigParam (const string & inParam) const
2394 {
2395  AJAAutoLock tmp(&mParamLock);
2396  return mParams.valueForKey(inParam);
2397 }
static PluginRegistry & Get(void)
static bool ParseQueryParams(const NTV2Dictionary &inSrcDict, NTV2Dictionary &outQueryParams)
Parses the string found in the given source dictionary&#39;s &#39;query&#39; key (kConnectParamQuery), storing the key/value pairs of all parsed query parameters into "outQueryParams". Query parameter values are URL-decoded before storing in the "outQueryParams" dictionary.
void * getFunctionAddress(const string &inFuncName)
bool useStdout(void) const
Everything needed to call CNTV2Card::ReadRegister or CNTV2Card::WriteRegister functions.
NTV2StringList loadedPlugins(void)
NTV2DeviceID DeviceID(void) const
#define kNTV2PluginRegInfoKey_LongName
Plugin long name.
Definition: ntv2nubaccess.h:79
#define kConnectParamDevSerial
Device with this serial number.
Definition: ntv2nubaccess.h:28
bool loadPlugin(const string &path, const string &folderPath, NTV2PluginPtr &outPtr, string &errMsg, const bool useStdout)
uint32_t gBaseConstructCount(0)
bool Allocate(const size_t inByteCount, const bool inPageAligned=false)
Allocates (or re-allocates) my user-space storage using the given byte count. I assume full responsib...
#define NBSFAIL(__x__)
bool(* fpGetRegistrationInfo)(const uint32_t, NTV2Dictionary &)
Obtains a plugin&#39;s registration information. Starting in SDK 17.1, all plugins must implement this fu...
virtual bool ConnectHasScheme(void) const
virtual AJAStatus SetThreadName(const char *name)
Definition: thread.cpp:176
virtual bool NTV2ReadRegisterMultiRemote(const ULWord numRegs, ULWord &outFailedRegNum, NTV2RegInfo outRegs[])
uint32_t gClientConstructCount(0)
virtual bool NTV2GetNumericParamRemote(const ULWord inParamID, ULWord &outValue)
unsigned long long stoull(const std::string &str, std::size_t *idx, int base)
Definition: common.cpp:154
void DumpLoadedPlugins(void)
NTV2DeviceIDSet NTV2GetSupportedDevices(const NTV2DeviceKinds inKinds=NTV2_DEVICEKIND_ALL)
Returns an NTV2DeviceIDSet of devices supported by the SDK.
Definition: ntv2utils.cpp:7397
virtual bool NTV2Connect(void)
NTV2FrameBufferFormat
Identifies a particular video frame buffer pixel format. See Device Frame Buffer Formats for details...
Definition: ntv2enums.h:223
#define kConnectParamPort
Port number (optional)
Definition: ntv2nubaccess.h:26
NTV2RPCClientAPI *(* fpCreateClient)(void *, const NTV2ConnectParams &, const uint32_t)
Instantiates a new client instance to talk to a remote server.
#define P_WARN(__x__)
NTV2StringSet::const_iterator NTV2StringSetConstIter
#define NBCINFO(__x__)
Declares the AJADebug class.
std::string valueForKey(const std::string &inKey) const
uint16_t u16ValueForKey(const std::string &inKey, const uint16_t inDefault=0) const
size_t size(void) const
#define kQParamShowX509Cert
Query parameter option that dumps X509 certificate info into message log.
Definition: ntv2nubaccess.h:37
#define AJAFUNC
Definition: ajatypes.h:306
bool pluginForPath(const string &path, NTV2PluginPtr &outHandle)
static AJAStatus Open(bool incrementRefCount=false)
Definition: debug.cpp:44
#define kNTV2PluginRegInfoKey_Copyright
Plugin copyright notice.
Definition: ntv2nubaccess.h:81
bool showParams(void) const
#define kNTV2PluginRegInfoKey_Vendor
Plugin vendor (manufacturer) name.
Definition: ntv2nubaccess.h:75
#define kQParamVerboseLogging
Query parameter option that enables verbose message logging.
Definition: ntv2nubaccess.h:35
NTV2StringList::const_iterator NTV2StringListConstIter
NTV2Plugin(void *handle, const string &path, const bool useStdout)
#define kNTV2PluginX500AttrKey_OrganizationName
Definition: ntv2nubaccess.h:96
static int32_t Decrement(int32_t volatile *pTarget)
Definition: atomic.cpp:95
the parser read a key of a value in an object
static string mbedErrStr(const int mbedtlsReturnCode)
#define kLegalSchemeNTV2Local
Definition: ntv2nubaccess.h:59
ULWord GetByteCount(void) const
#define kNTV2PluginRegInfoKey_ShortName
Plugin short name.
Definition: ntv2nubaccess.h:78
bool pluginIsLoaded(const string &path)
One-stop shop for parsing device specifications. (New in SDK 16.3) I do very little in the way of val...
#define PDBGX(__x__)
Declares the AJATime class.
virtual AJAStatus SetPriority(AJAThreadPriority priority)
Definition: thread.cpp:133
uint32_t gLoaderConstructCount(0)
virtual bool NTV2GetSupportedRemote(const ULWord inEnumsID, ULWordSet &outSupported)
size_t erase(const std::string &inKey)
Erases the given key and its corresponding value from me, returns 1 if successful, 0 if not.
bool string_to_wstring(const std::string &str, std::wstring &wstr)
Definition: common.cpp:248
virtual bool HasConnectParam(const std::string &inParam) const
static bool ExtractCertInfo(NTV2Dictionary &outInfo, const string &inStr)
Definition: lock.h:28
UWord DeviceIndex(void) const
virtual std::string ConnectParam(const std::string &inParam) const
Dict::const_iterator DictConstIter
Common base class for NTV2RPCClientAPI and NTV2RPCServerAPI.
Defines the AJARefPtr template class.
Definition: json.hpp:5362
bool showCertificate(void) const
virtual AJAStatus Start()
Definition: thread.cpp:91
NTV2RPCServerAPI(NTV2ConnectParams inParams, void *pRefCon)
My constructor.
bool hasPath(const string &path)
bool serialize(std::string &outStr) const
Serializes my contents into the given string, returns true if string is not empty.
#define false
#define kQParamDebugRegistry
Query parameter option that enables debugging of PluginRegistry.
Definition: ntv2nubaccess.h:39
uint32_t ULWord
Definition: ajatypes.h:236
NTV2Channel
These enum values are mostly used to identify a specific widget_framestore. They&#39;re also commonly use...
Definition: ntv2enums.h:1361
std::ostream & Print(std::ostream &oss, const bool inDumpResults=false) const
bool toHexString(std::string &outStr, const size_t inLineBreakInterval=0) const
Converts my contents into a hex-encoded string.
Declares NTV2 "nub" client functions.
std::string MakeDeviceSpec(const bool urlEncodeQuery) const
bool hasKey(const std::string &inKey) const
#define kConnectParamDevModel
First device of this model (e.g. &#39;kona4&#39;)
Definition: ntv2nubaccess.h:29
static bool ExtractIssuerInfo(NTV2Dictionary &outInfo, const string &inStr, const string &inParentKey)
AJALock mParamLock
Mutex to protect mParams.
virtual std::string Name(void) const
NTV2Dictionary mParams
Copy of config params passed to my constructor.
#define NTV2_ASSERT(_expr_)
Definition: ajatypes.h:489
virtual bool NTV2DMATransferRemote(const NTV2DMAEngine inDMAEngine, const bool inIsRead, const ULWord inFrameNumber, NTV2Buffer &inOutBuffer, const ULWord inCardOffsetBytes, const ULWord inNumSegments, const ULWord inSegmentHostPitch, const ULWord inSegmentCardPitch, const bool inSynchronous)
#define kConnectParamDevID
First device having this ID (e.g. &#39;0x10518400&#39;)
Definition: ntv2nubaccess.h:30
#define kConnectParamHost
DNS name, IPv4 or sw device DLL name.
Definition: ntv2nubaccess.h:25
uint32_t gPluginConstructCount(0)
bool Truncate(const size_t inByteCount)
Truncates me to the given length. No reallocation takes place.
#define kNTV2PluginSigFileKey_X509Certificate
X509 certificate (encoded as hex string)
Definition: ntv2nubaccess.h:89
#define PLGFAIL(__x__)
unsigned long stoul(const std::string &str, std::size_t *idx, int base)
Definition: common.cpp:143
NTV2RPCServerAPI *(* fpCreateServer)(void *, const NTV2ConfigParams &, const uint32_t)
Instantiates a new server instance for talking to clients.
virtual bool NTV2GetBoolParamRemote(const ULWord inParamID, ULWord &outValue)
virtual std::ostream & Print(std::ostream &oss) const
virtual bool Active()
Definition: thread.cpp:116
void * addressForSymbol(const string &inSymbol, string &outErrorMsg)
bool Fill(const T &inValue)
Fills me with the given scalar value.
std::string PercentEncode(const std::string &inStr)
bool GetString(std::string &outString, const size_t inU8Offset=0, const size_t inMaxSize=128) const
Answers with my contents as a character string.
static std::string AJAFingerprint(const bool inLowerCase=false, const bool inStripColons=false)
#define NBSWARN(__x__)
An object that can connect to, and operate remote or fake devices. I have three general API groups: ...
NTV2StringSet keys(void) const
#define true
#define kFuncNameCreateServer
Create an NTV2RPCServerAPI instance.
Definition: ntv2nubaccess.h:63
std::string NTV2Version(const bool inDetailed=false)
Definition: ntv2version.cpp:41
bool insert(const std::string &inKey, const std::string &inValue)
Stores the given value using the given key; overwrites existing value if already present.
#define kNTV2PluginInfoKey_Fingerprint
Issuer cert fingerprint.
Definition: ntv2nubaccess.h:71
NTV2PluginLoader(NTV2Dictionary &params)
NTV2DeviceID
Identifies a specific AJA NTV2 device model number. The NTV2DeviceID is actually the PROM part number...
Definition: ntv2enums.h:20
static void EnableDebugging(const bool inEnable=true)
virtual bool NTV2Disconnect(void)
Disconnects me from the remote/fake host, closing the connection.
Declares the AJAThread class.
virtual void RunServer(void)
Principal server thread function, subclsses should override.
bool SetParams(const NTV2ConfigParams &inNewParams, const bool inAugment=false)
virtual void Stop(void)
Call this to request the server to stop.
virtual ~NTV2RPCClientAPI()
My destructor, automatically calls NTV2Disconnect.
virtual bool NTV2DriverGetBitFileInformationRemote(BITFILE_INFO_STRUCT &nfo, const NTV2BitFileType typ)
void Reset(const std::string inSpec="")
Resets me, then parses the given device specification.
virtual std::ostream & Print(std::ostream &oss) const
NTV2DeviceIDSet::const_iterator NTV2DeviceIDSetConstIter
A convenient const iterator for NTV2DeviceIDSet.
Definition: ntv2utils.h:1100
bool indexForPath(const string &path, size_t &outIndex)
std::set< std::string > NTV2StringSet
size_t addFrom(const NTV2Dictionary &inDict)
Adds all values from inDict with non-matching keys, ignoring all matching keys.
uint32_t gServerDestructCount(0)
uint32_t gClientDestructCount(0)
virtual bool NTV2MessageRemote(NTV2_HEADER *pInMessage)
size_t largestKeySize(void) const
std::string HostWithPort(void) const
bool isLoaded(void) const
NTV2DMAEngine
Definition: ntv2enums.h:1861
#define AJA_NULL
Definition: ajatypes.h:180
#define kNTV2PluginInfoKey_Errors
Plugin load or validation error(s), if any.
Definition: ntv2nubaccess.h:72
Declares the most fundamental data types used by NTV2. Since Windows NT was the first principal devel...
void clear(void)
Removes all of my key/value pairs.
Defines for the NTV2 SDK version number, used by ajantv2/includes/ntv2enums.h. See the ajantv2/includ...
enum _INTERRUPT_ENUMS_ INTERRUPT_ENUMS
static void Sleep(const int32_t inMilliseconds)
Suspends execution of the current thread for a given number of milliseconds.
Definition: systemtime.cpp:284
virtual bool NTV2CloseRemote(void)
std::ostream & Print(std::ostream &oss, const bool inCompact=true) const
Prints human-readable representation to ostream.
void split(const std::string &str, const char delim, std::vector< std::string > &elems)
Definition: common.cpp:350
#define kNTV2PluginX500AttrKey_OrganizationalUnitName
Definition: ntv2nubaccess.h:97
bool isVerbose(void) const
std::set< ULWord > ULWordSet
A collection of unique ULWord (uint32_t) values.
bool getBaseNameFromScheme(string &outName) const
#define kNTV2PluginRegInfoKey_Description
Brief plugin description.
Definition: ntv2nubaccess.h:80
All new NTV2 structs start with this common header.
virtual bool IsConnected(void) const
the parser finished reading a JSON value
NTV2RPCClientAPI(NTV2ConnectParams inParams, void *pRefCon)
My constructor.
static bool DebuggingEnabled(void)
bool useStdout(void) const
#define kQParamVDevFileName
.vdev file name (with extension)
Definition: ntv2nubaccess.h:45
static int32_t Increment(int32_t volatile *pTarget)
Definition: atomic.cpp:82
std::string Resource(const bool inStripLeadSlash=true) const
virtual bool NTV2WaitForInterruptRemote(const INTERRUPT_ENUMS eInterrupt, const ULWord timeOutMs)
std::string NTV2GetPluginsFolderPath(const bool inAddTrailingPathDelim=false)
Definition: ntv2utils.cpp:7371
uint32_t * refConForPath(const string &path)
uint32_t mSpare[1024]
Reserved.
static NTV2RPCClientAPI * CreateClient(NTV2ConnectParams &inParams)
Instantiates a new NTV2RPCClientAPI instance using the given NTV2ConnectParams.
#define kNTV2PluginRegInfoKey_CommonName
Plugin vendor domain name.
Definition: ntv2nubaccess.h:76
uint64_t ULWord64
Definition: ajatypes.h:239
Base class of objects that can serve device operation RPCs with NTV2RPCClientAPI instances.
#define kNTV2PluginInfoKey_PluginPath
Local host full path to plugin file.
Definition: ntv2nubaccess.h:68
virtual bool IsRunning(void) const
#define INSTP(_p_)
NTV2BitFileType
Definition: ntv2enums.h:3354
virtual NTV2ConfigParams ConfigParams(void) const
static void Terminate(void)
virtual ~NTV2RPCBase()
virtual bool NTV2ReadRegisterRemote(const ULWord regNum, ULWord &outRegValue, const ULWord regMask, const ULWord regShift)
#define kQParamLogToStdout
Query parameter option that logs messages to standard output.
Definition: ntv2nubaccess.h:36
#define NBCFAIL(__x__)
#define P_INFO(__x__)
ULWord countForPath(const string &path)
void * refCon(void) const
bool mTerminate
Set true to stop server.
Describes a user-space buffer on the host computer. I have an address and a length, plus some optional attributes (allocated by SDK?, page-aligned? etc.).
bool deserialize(const std::string &inStr)
Resets me from the given string.
#define AJA_sERROR(_index_, _expr_)
Definition: debug.h:176
std::string MakeQueryString(const bool urlEncode) const
bool getPluginsFolder(string &outPath) const
string pluginSigPath(void) const
#define kNTV2PluginInfoKey_PluginBaseName
Plugin base name (i.e. without extension)
Definition: ntv2nubaccess.h:70
std::string NTV2DeviceIDToString(const NTV2DeviceID inValue, const bool inForRetailDisplay=false)
Definition: ntv2utils.cpp:4608
#define kFuncNameGetRegInfo
Answers with plugin registration info.
Definition: ntv2nubaccess.h:64
virtual bool NTV2DriverGetBuildInformationRemote(BUILD_INFO_STRUCT &buildInfo)
Declares the AJAAtomic class.
#define P_NOTE(__x__)
std::string & strip(std::string &str, const std::string &ws)
Definition: common.cpp:461
#define kConnectParamDevIndex
Device having this index number.
Definition: ntv2nubaccess.h:27
#define kNTV2PluginInfoKey_PluginsPath
Local host full path to folder containing plugins.
Definition: ntv2nubaccess.h:67
#define kQParamShowParams
Query parameter option that dumps parameters into message log.
Definition: ntv2nubaccess.h:38
virtual std::string Description(void) const
bool unloadPlugin(const string &path, string &errMsg)
std::string PercentDecode(const std::string &inStr)
bool isValidated(void) const
#define DEC(__x__)
virtual bool NTV2OpenRemote(void)
NTV2RPCBase(NTV2Dictionary params, uint32_t *pRefCon)
#define P_DBG(__x__)
#define kNTV2PluginX500AttrKey_CommonName
Definition: ntv2nubaccess.h:93
std::ostream & PrintErrors(std::ostream &oss) const
bool empty(void) const
AJARefPtr< NTV2Plugin > NTV2PluginPtr
#define kNTV2PluginRegInfoKey_OrgUnit
Plugin organization unit (to match certificate subject OU)
Definition: ntv2nubaccess.h:77
static bool LoadPlugin(const string &path, const string &folderPath, NTV2PluginPtr &outPtr, string &outErrMsg, const bool inUseStdout)
Declares numerous NTV2 utility functions.
virtual ~NTV2RPCServerAPI()
My destructor, automatically calls NTV2Disconnect.
uint32_t * mpRefCon
Reserved for internal use.
virtual AJAStatus Attach(AJAThreadFunction *pThreadFunction, void *pUserContext)
Definition: thread.cpp:169
virtual bool NTV2GetDriverVersionRemote(ULWord &vers)
Declares the AJASystemInfo class.
size_t updateFrom(const NTV2Dictionary &inDict)
Updates all values from inDict with matching keys, ignoring all non-matching keys.
uint32_t gLoaderDestructCount(0)
#define kConnectParamScheme
URL scheme.
Definition: ntv2nubaccess.h:24
virtual bool HasConfigParam(const std::string &inParam) const
AJARefPtr< PluginRegistry > PluginRegistryPtr
#define AJA_NTV2_SDK_VERSION
Definition: ntv2version.h:20
size_t largestValueSize(void) const
uint16_t UWord
Definition: ajatypes.h:234
#define kNTV2PluginRegInfoKey_Version
Plugin version (string)
Definition: ntv2nubaccess.h:83
#define PLGDBG(__x__)
bool mRunning
Running?
NTV2DeviceSpecParser(const std::string inSpec="")
My constructor. If given device specification is non-empty, proceeds to Parse it. ...
#define xHEX0N(__x__, __n__)
A simple (not thread-safe) set of key/value pairs. (New in SDK 16.3)
#define SIG_EXTENSION
#define PLGWARN(__x__)
virtual bool NTV2WriteRegisterRemote(const ULWord regNum, const ULWord regValue, const ULWord regMask, const ULWord regShift)
Private include file for all ajabase sources.
#define DLL_EXTENSION
std::vector< uint32_t > ULWordSequence
An ordered sequence of ULWord (uint32_t) values.
#define kNTV2PluginSigFileKey_Signature
X509 digital signature (encoded as hex string)
Definition: ntv2nubaccess.h:90
std::string & upper(std::string &str)
Definition: common.cpp:442
#define kNTV2PluginRegInfoKey_NTV2SDKVersion
NTV2 SDK version that plugin was compiled with.
Definition: ntv2nubaccess.h:82
virtual AJAStatus GetValue(const AJASystemInfoTag inTag, std::string &outValue) const
Answers with the host system info value string for the given AJASystemInfoTag.
Definition: info.cpp:153
#define kFuncNameCreateClient
Create an NTV2RPCClientAPI instance.
Definition: ntv2nubaccess.h:62
std::string join(const std::vector< std::string > &parts, const std::string &delim)
Definition: common.cpp:468
#define P_FAIL(__x__)
std::set< NTV2DeviceID > NTV2DeviceIDSet
A set of NTV2DeviceIDs.
Definition: ntv2utils.h:1098
void * GetHostPointer(void) const
string pluginsPath(void) const
uint32_t gBaseDestructCount(0)
void * getSymbolAddress(const string &inSymbolName, string &outErrorMsg)
std::vector< std::string > NTV2StringList
#define HEX0N(__x__, __n__)
Definition: debug.cpp:1181
uint32_t gPluginDestructCount(0)
string pluginPath(void) const
std::string & lower(std::string &str)
Definition: common.cpp:436
virtual NTV2ConnectParams ConnectParams(void) const
std::string InfoString(void) const
static NTV2RPCServerAPI * CreateServer(NTV2ConfigParams &inParams)
Factory method that instantiates a new NTV2RPCServerAPI instance using a plugin based on the specifie...
Declares enums and structs used by all platform drivers and the SDK.
virtual bool NTV2AutoCirculateRemote(AUTOCIRCULATE_DATA &autoCircData)
#define kConnectParamQuery
Query – everything past &#39;?&#39; in URL.
Definition: ntv2nubaccess.h:32
virtual std::string ConfigParam(const std::string &inParam) const
#define NBSDBG(__x__)
std::string & replace(std::string &str, const std::string &from, const std::string &to)
Definition: common.cpp:110
static std::string ShortSDKVersion(void)
uint32_t gServerConstructCount(0)
#define kConnectParamResource
Resource path – everything past URL [scheme://host[:port]/], excluding [?query]. ...
Definition: ntv2nubaccess.h:31
#define AJA_sDEBUG(_index_, _expr_)
Definition: debug.h:220
NTV2StringList pluginStats(void)
virtual bool NTV2DownloadTestPatternRemote(const NTV2Channel ch, const NTV2PixelFormat pf, const UWord msk, const bool dma, const ULWord tpNum)
string pluginBaseName(void) const
#define kNTV2PluginInfoKey_PluginSigPath
Local host full path to plugin signature file.
Definition: ntv2nubaccess.h:69