workunitservices.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems®.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. /* TBD
  14. FilesRead
  15. FilesWritten
  16. Errors
  17. Plugins
  18. Results
  19. Timings
  20. Persists changed?
  21. */
  22. #pragma warning (disable : 4786)
  23. #pragma warning (disable : 4297) // function assumed not to throw an exception but does
  24. #include "platform.h"
  25. #include "workunit.hpp"
  26. #include "agentctx.hpp"
  27. #include "enginecontext.hpp"
  28. #include "portlist.h"
  29. #include "jio.hpp"
  30. #include "jmisc.hpp"
  31. #include "jstring.hpp"
  32. #include "dasess.hpp"
  33. #include "dasds.hpp"
  34. #include "dautils.hpp"
  35. #include "daaudit.hpp"
  36. #include "sacmd.hpp"
  37. #include "workunitservices.hpp"
  38. #include "workunitservices.ipp"
  39. #include "environment.hpp"
  40. #include "seclib.hpp"
  41. #define WORKUNITSERVICES_VERSION "WORKUNITSERVICES 1.0.2"
  42. static const char * compatibleVersions[] = {
  43. NULL };
  44. static const char * EclDefinition =
  45. "export WsWorkunitRecord := record "
  46. " string24 wuid;"
  47. " string owner{maxlength(64)};"
  48. " string cluster{maxlength(64)};"
  49. " string roxiecluster{maxlength(64)};"
  50. " string job{maxlength(256)};"
  51. " string10 state;"
  52. " string7 priority;"
  53. " integer2 priorityvalue;"
  54. " string20 created;"
  55. " string20 modified;"
  56. " boolean online;"
  57. " boolean protected;"
  58. " end;\n"
  59. "export WsTimeStamp := record "
  60. " string32 application;"
  61. " string16 id;"
  62. " string20 time;"
  63. " string16 instance;"
  64. " end;\n"
  65. "export WsMessage := record "
  66. " unsigned4 severity;"
  67. " integer4 code;"
  68. " string32 location;"
  69. " unsigned4 row;"
  70. " unsigned4 col;"
  71. " string16 source;"
  72. " string20 time;"
  73. " string message{maxlength(1024)};"
  74. " end;\n"
  75. "export WsFileRead := record "
  76. " string name{maxlength(256)};"
  77. " string cluster{maxlength(64)};"
  78. " boolean isSuper;"
  79. " unsigned4 usage;"
  80. " end;\n"
  81. "export WsFileWritten := record "
  82. " string name{maxlength(256)};"
  83. " string10 graph;"
  84. " string cluster{maxlength(64)};"
  85. " unsigned4 kind;"
  86. " end;\n"
  87. "export WsTiming := record "
  88. " unsigned4 count;"
  89. " unsigned4 duration;"
  90. " unsigned4 max;"
  91. " string name{maxlength(64)};"
  92. " end;\n"
  93. "export WsStatistic := record "
  94. " unsigned8 value;"
  95. " unsigned8 count;"
  96. " unsigned8 maxValue;"
  97. " string creatorType;"
  98. " string creator;"
  99. " string scopeType;"
  100. " string scope;"
  101. " string name;"
  102. " string description;"
  103. " string unit;"
  104. " end;\n"
  105. "export WorkunitServices := SERVICE :time, cpp\n"
  106. " boolean WorkunitExists(const varstring wuid, boolean online=true, boolean archived=false) : context,entrypoint='wsWorkunitExists'; \n"
  107. " dataset(WsWorkunitRecord) WorkunitList("
  108. " const varstring lowwuid='',"
  109. " const varstring highwuid='',"
  110. " const varstring username='',"
  111. " const varstring cluster='',"
  112. " const varstring jobname='',"
  113. " const varstring state='',"
  114. " const varstring priority='',"
  115. " const varstring fileread='',"
  116. " const varstring filewritten='',"
  117. " const varstring roxiecluster='',"
  118. " const varstring eclcontains='',"
  119. " boolean online=true,"
  120. " boolean archived=false,"
  121. " const varstring appvalues=''"
  122. ") : context,entrypoint='wsWorkunitList'; \n"
  123. " varstring WUIDonDate(unsigned4 year,unsigned4 month,unsigned4 day,unsigned4 hour, unsigned4 minute) : entrypoint='wsWUIDonDate'; \n"
  124. " varstring WUIDdaysAgo(unsigned4 daysago) : entrypoint='wsWUIDdaysAgo'; \n"
  125. " dataset(WsTimeStamp) WorkunitTimeStamps(const varstring wuid) : context,entrypoint='wsWorkunitTimeStamps'; \n"
  126. " dataset(WsMessage) WorkunitMessages(const varstring wuid) : context,entrypoint='wsWorkunitMessages'; \n"
  127. " dataset(WsFileRead) WorkunitFilesRead(const varstring wuid) : context,entrypoint='wsWorkunitFilesRead'; \n"
  128. " dataset(WsFileWritten) WorkunitFilesWritten(const varstring wuid) : context,entrypoint='wsWorkunitFilesWritten'; \n"
  129. " dataset(WsTiming) WorkunitTimings(const varstring wuid) : context,entrypoint='wsWorkunitTimings'; \n"
  130. " streamed dataset(WsStatistic) WorkunitStatistics(const varstring wuid, boolean includeActivities = false, const varstring _filter = '') : context,entrypoint='wsWorkunitStatistics'; \n"
  131. " boolean setWorkunitAppValue(const varstring app, const varstring key, const varstring value, boolean overwrite=true) : context,entrypoint='wsSetWorkunitAppValue'; \n"
  132. "END;";
  133. #define WAIT_SECONDS 30
  134. #define SDS_LOCK_TIMEOUT (1000*60*5)
  135. #define SASHA_TIMEOUT (1000*60*10)
  136. WORKUNITSERVICES_API bool getECLPluginDefinition(ECLPluginDefinitionBlock *pb)
  137. {
  138. if (pb->size == sizeof(ECLPluginDefinitionBlockEx))
  139. {
  140. ECLPluginDefinitionBlockEx * pbx = (ECLPluginDefinitionBlockEx *) pb;
  141. pbx->compatibleVersions = compatibleVersions;
  142. }
  143. else if (pb->size != sizeof(ECLPluginDefinitionBlock))
  144. return false;
  145. pb->magicVersion = PLUGIN_VERSION;
  146. pb->version = WORKUNITSERVICES_VERSION;
  147. pb->moduleName = "lib_WORKUNITSERVICES";
  148. pb->ECL = EclDefinition;
  149. pb->flags = PLUGIN_IMPLICIT_MODULE;
  150. pb->description = "WORKUNITSERVICES library";
  151. return true;
  152. }
  153. namespace nsWorkunitservices {
  154. IPluginContext * parentCtx = NULL;
  155. static void getSashaNodes(SocketEndpointArray &epa)
  156. {
  157. Owned<IEnvironmentFactory> factory = getEnvironmentFactory(true);
  158. Owned<IConstEnvironment> env = factory->openEnvironment();
  159. Owned<IPropertyTree> root = &env->getPTree();
  160. StringBuffer tmp;
  161. Owned<IPropertyTreeIterator> siter = root->getElements("Software/SashaServerProcess/Instance");
  162. ForEach(*siter) {
  163. if (siter->query().getProp("@netAddress",tmp.clear())) {
  164. SocketEndpoint sashaep(tmp.str(),siter->query().getPropInt("@port",DEFAULT_SASHA_PORT));
  165. epa.append(sashaep);
  166. }
  167. }
  168. }
  169. static IWorkUnitFactory * getWorkunitFactory(ICodeContext * ctx)
  170. {
  171. IEngineContext *engineCtx = ctx->queryEngineContext();
  172. if (engineCtx && !engineCtx->allowDaliAccess())
  173. {
  174. Owned<IException> e = MakeStringException(-1, "workunitservices cannot access Dali in this context - this normally means it is being called from a thor slave");
  175. EXCLOG(e, NULL);
  176. throw e.getClear();
  177. }
  178. //MORE: These should really be set up correctly - probably should be returned from IEngineContext
  179. ISecManager *secmgr = NULL;
  180. ISecUser *secuser = NULL;
  181. return getWorkUnitFactory(secmgr, secuser);
  182. }
  183. static bool securityDisabled = false;
  184. static bool checkScopeAuthorized(IUserDescriptor *user, const char *scopename)
  185. {
  186. if (securityDisabled)
  187. return true;
  188. unsigned auditflags = DALI_LDAP_AUDIT_REPORT|DALI_LDAP_READ_WANTED;
  189. SecAccessFlags perm = SecAccess_Full;
  190. if (scopename && *scopename)
  191. {
  192. //Create signature
  193. CDateTime now;
  194. StringBuffer b64sig;
  195. createDaliSignature(scopename, user, now, b64sig);
  196. perm = querySessionManager().getPermissionsLDAP("workunit",scopename,user,auditflags, b64sig.str(), now);
  197. if (perm<0)
  198. {
  199. if (perm == SecAccess_Unavailable)
  200. {
  201. perm = SecAccess_Full;
  202. securityDisabled = true;
  203. }
  204. else
  205. perm = SecAccess_None;
  206. }
  207. if (!HASREADPERMISSION(perm))
  208. return false;
  209. }
  210. return true;
  211. }
  212. static IConstWorkUnit * getWorkunit(ICodeContext * ctx, const char * wuid)
  213. {
  214. StringBuffer _wuid(wuid);
  215. if (!_wuid.length())
  216. return NULL;
  217. wuid = _wuid.toUpperCase().str();
  218. Owned<IWorkUnitFactory> wuFactory = getWorkunitFactory(ctx);
  219. Owned<IConstWorkUnit> wu = wuFactory->openWorkUnit(wuid);
  220. if (wu)
  221. {
  222. if (!checkScopeAuthorized(ctx->queryUserDescriptor(), wu->queryWuScope()))
  223. wu.clear();
  224. }
  225. return wu.getClear();
  226. }
  227. static IConstWorkUnit *getWorkunit(ICodeContext * ctx)
  228. {
  229. StringAttr wuid;
  230. wuid.setown(ctx->getWuid());
  231. // One assumes we have read access to our own wu
  232. return getWorkunit(ctx, wuid);
  233. }
  234. static StringBuffer &getWUIDonDate(StringBuffer &wuid,unsigned year,unsigned month,unsigned day,unsigned hour,unsigned minute)
  235. {
  236. if ((year==0)||(month==0)||(day==0)) {
  237. CDateTime dt;
  238. dt.setNow();
  239. unsigned y;
  240. unsigned m;
  241. unsigned d;
  242. dt.getDate(y,m,d, true);
  243. if (year==0)
  244. year = y;
  245. if (month==0)
  246. month = m;
  247. if (day==0)
  248. day = d;
  249. }
  250. else if (year<100)
  251. year+=2000;
  252. wuid.appendf("W%d%02d%02d-%02d%02d00",year,month,day,hour,minute);
  253. return wuid;
  254. }
  255. static StringBuffer &getWUIDdaysAgo(StringBuffer &wuid,int daysago)
  256. {
  257. CDateTime dt;
  258. dt.setNow();
  259. dt.adjustTime(-(daysago*60*24));
  260. unsigned y;
  261. unsigned m;
  262. unsigned d;
  263. dt.getDate(y,m,d, true);
  264. unsigned h;
  265. unsigned mn;
  266. unsigned s;
  267. unsigned ns;
  268. dt.getTime(h,mn,s,ns,true);
  269. return getWUIDonDate(wuid,y,m,d,h,mn);
  270. }
  271. static bool addWUQueryFilter(WUSortField *filters, unsigned &count, MemoryBuffer &buff, const char *name, WUSortField value)
  272. {
  273. if (!name || !*name)
  274. return false;
  275. filters[count++] = value;
  276. buff.append(name);
  277. return true;
  278. }
  279. static bool serializeWUInfo(IConstWorkUnitInfo &info,MemoryBuffer &mb)
  280. {
  281. fixedAppend(mb,24,info.queryWuid());
  282. varAppendMax(mb,64,info.queryUser());
  283. varAppendMax(mb,64,info.queryClusterName());
  284. varAppendMax(mb,64,""); // roxiecluster is obsolete
  285. varAppendMax(mb,256,info.queryJobName());
  286. fixedAppend(mb,10,info.queryStateDesc());
  287. fixedAppend(mb,7,info.queryPriorityDesc());
  288. short int prioritylevel = info.getPriorityLevel();
  289. mb.append(prioritylevel);
  290. fixedAppend(mb,20,""); // Created timestamp
  291. fixedAppend(mb,20,""); // Modified timestamp
  292. mb.append(true);
  293. mb.append(info.isProtected());
  294. if (mb.length()>WORKUNIT_SERVICES_BUFFER_MAX) {
  295. mb.clear().append(WUS_STATUS_OVERFLOWED);
  296. return false;
  297. }
  298. return true;
  299. }
  300. }//namespace
  301. using namespace nsWorkunitservices;
  302. static const unsigned MAX_FILTERS=20;
  303. WORKUNITSERVICES_API void wsWorkunitList(
  304. ICodeContext *ctx,
  305. size32_t & __lenResult,
  306. void * & __result,
  307. const char *lowwuid,
  308. const char *highwuid,
  309. const char *username,
  310. const char *cluster,
  311. const char *jobname,
  312. const char *state,
  313. const char *priority,
  314. const char *fileread,
  315. const char *filewritten,
  316. const char *roxiecluster, // Not in use - retained for compatibility only
  317. const char *eclcontains,
  318. bool online,
  319. bool archived,
  320. const char *appvalues
  321. )
  322. {
  323. MemoryBuffer mb;
  324. if (archived) {
  325. SocketEndpointArray sashaeps;
  326. getSashaNodes(sashaeps);
  327. ForEachItemIn(i,sashaeps) {
  328. Owned<ISashaCommand> cmd = createSashaCommand();
  329. cmd->setAction(SCA_WORKUNIT_SERVICES_GET);
  330. cmd->setOnline(false);
  331. cmd->setArchived(true);
  332. cmd->setWUSmode(true);
  333. if (lowwuid&&*lowwuid)
  334. cmd->setAfter(lowwuid);
  335. if (highwuid&&*highwuid)
  336. cmd->setBefore(highwuid);
  337. if (username&&*username)
  338. cmd->setOwner(username);
  339. if (cluster&&*cluster)
  340. cmd->setCluster(cluster);
  341. if (jobname&&*jobname)
  342. cmd->setJobName(jobname);
  343. if (state&&*state)
  344. cmd->setState(state);
  345. if (priority&&*priority)
  346. cmd->setPriority(priority);
  347. if (fileread&&*fileread)
  348. cmd->setFileRead(fileread);
  349. if (filewritten&&*filewritten)
  350. cmd->setFileWritten(filewritten);
  351. if (eclcontains&&*eclcontains)
  352. cmd->setEclContains(eclcontains);
  353. Owned<INode> sashanode = createINode(sashaeps.item(i));
  354. if (cmd->send(sashanode,SASHA_TIMEOUT)) {
  355. byte res = cmd->getWUSresult(mb);
  356. if (res==WUS_STATUS_OVERFLOWED)
  357. throw MakeStringException(-1,"WORKUNITSERVICES: Result buffer overflowed");
  358. if (res!=WUS_STATUS_OK)
  359. throw MakeStringException(-1,"WORKUNITSERVICES: Sasha get results failed (%d)",(int)res);
  360. break;
  361. }
  362. if (i+1>=sashaeps.ordinality()) {
  363. StringBuffer ips;
  364. sashaeps.item(0).getIpText(ips);
  365. throw MakeStringException(-1,"Time out to Sasha server on %s (server not running or query too complex)",ips.str());
  366. }
  367. }
  368. }
  369. if (online)
  370. {
  371. WUSortField filters[MAX_FILTERS+1]; // NOTE - increase if you add a LOT more parameters! The +1 is to allow space for the terminator
  372. unsigned filterCount = 0;
  373. MemoryBuffer filterbuf;
  374. if (state && *state)
  375. {
  376. filters[filterCount++] = WUSFstate;
  377. if (!strieq(state, "unknown"))
  378. filterbuf.append(state);
  379. else
  380. filterbuf.append("");
  381. }
  382. if (priority && *priority)
  383. {
  384. filters[filterCount++] = WUSFpriority;
  385. if (!strieq(priority, "unknown"))
  386. filterbuf.append(priority);
  387. else
  388. filterbuf.append("");
  389. }
  390. addWUQueryFilter(filters, filterCount, filterbuf, cluster, WUSFcluster);
  391. addWUQueryFilter(filters, filterCount, filterbuf, fileread, (WUSortField) (WUSFfileread | WUSFnocase));
  392. addWUQueryFilter(filters, filterCount, filterbuf, filewritten, (WUSortField) (WUSFfilewritten | WUSFnocase));
  393. addWUQueryFilter(filters, filterCount, filterbuf, username, (WUSortField) (WUSFuser | WUSFnocase));
  394. addWUQueryFilter(filters, filterCount, filterbuf, jobname, (WUSortField) (WUSFjob | WUSFwild | WUSFnocase));
  395. addWUQueryFilter(filters, filterCount, filterbuf, eclcontains, (WUSortField) (WUSFecl | WUSFwild));
  396. addWUQueryFilter(filters, filterCount, filterbuf, lowwuid, WUSFwuid);
  397. addWUQueryFilter(filters, filterCount, filterbuf, highwuid, WUSFwuidhigh);
  398. if (appvalues && *appvalues)
  399. {
  400. StringArray appFilters;
  401. appFilters.appendList(appvalues, "|"); // Multiple filters separated by |
  402. ForEachItemIn(idx, appFilters)
  403. {
  404. StringArray appFilter; // individual filter of form appname/key=value or appname/*=value
  405. appFilter.appendList(appFilters.item(idx), "=");
  406. const char *appvalue;
  407. switch (appFilter.length())
  408. {
  409. case 1:
  410. appvalue = NULL;
  411. break;
  412. case 2:
  413. appvalue = appFilter.item(1);
  414. break;
  415. default:
  416. throw MakeStringException(-1,"WORKUNITSERVICES: Invalid application value filter %s (expected format is 'appname/keyname=value')", appFilters.item(idx));
  417. }
  418. const char *appkey = appFilter.item(0);
  419. if (!strchr(appkey, '/'))
  420. throw MakeStringException(-1,"WORKUNITSERVICES: Invalid application value filter %s (expected format is 'appname/keyname=value')", appFilters.item(idx));
  421. if (filterCount>=MAX_FILTERS)
  422. throw MakeStringException(-1,"WORKUNITSERVICES: Too many filters");
  423. filterbuf.append(appkey);
  424. filterbuf.append(appvalue);
  425. filters[filterCount++] = WUSFappvalue;
  426. }
  427. }
  428. filters[filterCount] = WUSFterm;
  429. Owned<IWorkUnitFactory> wuFactory = getWorkunitFactory(ctx);
  430. Owned<IConstWorkUnitIterator> it = wuFactory->getWorkUnitsSorted((WUSortField) (WUSFwuid | WUSFreverse), filters, filterbuf.bufferBase(), 0, INT_MAX, NULL, NULL); // MORE - need security flags here!
  431. ForEach(*it)
  432. {
  433. if (!serializeWUInfo(it->query(), mb))
  434. throw MakeStringException(-1,"WORKUNITSERVICES: Result buffer overflowed");
  435. }
  436. }
  437. __lenResult = mb.length();
  438. __result = mb.detach();
  439. }
  440. WORKUNITSERVICES_API bool wsWorkunitExists(ICodeContext *ctx, const char *wuid, bool online, bool archived)
  441. {
  442. if (!wuid||!*wuid)
  443. return false;
  444. StringBuffer _wuid(wuid);
  445. wuid = _wuid.toUpperCase().str();
  446. if (online)
  447. {
  448. Owned<IWorkUnitFactory> wuFactory = getWorkunitFactory(ctx);
  449. Owned<IConstWorkUnit> wu = wuFactory->openWorkUnit(wuid); // Note - we don't use getWorkUnit as we don't need read access
  450. return wu != NULL;
  451. }
  452. if (archived)
  453. {
  454. SocketEndpointArray sashaeps;
  455. getSashaNodes(sashaeps);
  456. ForEachItemIn(i,sashaeps) {
  457. Owned<ISashaCommand> cmd = createSashaCommand();
  458. cmd->setAction(SCA_LIST);
  459. cmd->setOnline(false);
  460. cmd->setArchived(true);
  461. cmd->addId(wuid);
  462. Owned<INode> sashanode = createINode(sashaeps.item(i));
  463. if (cmd->send(sashanode,SASHA_TIMEOUT)) {
  464. return cmd->numIds()>0;
  465. }
  466. }
  467. }
  468. return false;
  469. }
  470. WORKUNITSERVICES_API char * wsWUIDonDate(unsigned year,unsigned month,unsigned day,unsigned hour,unsigned minute)
  471. {
  472. StringBuffer ret;
  473. return getWUIDonDate(ret,year,month,day,hour,minute).detach();
  474. }
  475. WORKUNITSERVICES_API char * wsWUIDdaysAgo(unsigned daysago)
  476. {
  477. StringBuffer ret;
  478. return getWUIDdaysAgo(ret,(int)daysago).detach();
  479. }
  480. class WsTimeStampVisitor : public WuScopeVisitorBase
  481. {
  482. public:
  483. WsTimeStampVisitor(MemoryBuffer & _mb) : mb(_mb) {}
  484. virtual void noteStatistic(StatisticKind kind, unsigned __int64 value, IConstWUStatistic & cur) override
  485. {
  486. const char * curScope = cur.queryScope();
  487. const char * kindName = queryStatisticName(kind);
  488. assertex(kindName && memicmp(kindName, "when", 4) == 0);
  489. kindName += 4;
  490. StringBuffer formattedTime;
  491. convertTimestampToStr(value, formattedTime, true);
  492. SCMStringBuffer creator;
  493. cur.getCreator(creator);
  494. const char * at = strchr(creator.str(), '@');
  495. const char * instance = at ? at + 1 : creator.str();
  496. fixedAppend(mb, 32, curScope);
  497. fixedAppend(mb, 16, kindName); // id
  498. fixedAppend(mb, 20, formattedTime); // time
  499. fixedAppend(mb, 16, instance); // item correct here
  500. }
  501. protected:
  502. MemoryBuffer & mb;
  503. };
  504. WORKUNITSERVICES_API void wsWorkunitTimeStamps(ICodeContext *ctx, size32_t & __lenResult, void * & __result, const char *wuid)
  505. {
  506. Owned<IConstWorkUnit> wu = getWorkunit(ctx, wuid);
  507. MemoryBuffer mb;
  508. if (wu)
  509. {
  510. WsTimeStampVisitor visitor(mb);
  511. WuScopeFilter filter("measure[When],source[global]");
  512. Owned<IConstWUScopeIterator> iter = &wu->getScopeIterator(filter);
  513. ForEach(*iter)
  514. {
  515. iter->playProperties(visitor);
  516. }
  517. }
  518. __lenResult = mb.length();
  519. __result = mb.detach();
  520. }
  521. WORKUNITSERVICES_API void wsWorkunitMessages( ICodeContext *ctx, size32_t & __lenResult, void * & __result, const char *wuid )
  522. {
  523. Owned<IConstWorkUnit> wu = getWorkunit(ctx, wuid);
  524. MemoryBuffer mb;
  525. if (wu)
  526. {
  527. SCMStringBuffer s;
  528. Owned<IConstWUExceptionIterator> exceptions = &wu->getExceptions();
  529. ForEach(*exceptions)
  530. {
  531. IConstWUException &e = exceptions->query();
  532. mb.append((unsigned) e.getSeverity());
  533. mb.append((int) e.getExceptionCode());
  534. e.getExceptionFileName(s);
  535. fixedAppend(mb, 32, s.str(), s.length());
  536. mb.append((unsigned) e.getExceptionLineNo());
  537. mb.append((unsigned) e.getExceptionColumn());
  538. e.getExceptionSource(s);
  539. fixedAppend(mb, 16, s.str(), s.length());
  540. e.getTimeStamp(s);
  541. fixedAppend(mb, 20, s.str(), s.length());
  542. e.getExceptionMessage(s);
  543. varAppendMax(mb, 1024, s.str(), s.length());
  544. }
  545. }
  546. __lenResult = mb.length();
  547. __result = mb.detach();
  548. }
  549. WORKUNITSERVICES_API void wsWorkunitFilesRead( ICodeContext *ctx, size32_t & __lenResult, void * & __result, const char *wuid )
  550. {
  551. MemoryBuffer mb;
  552. Owned<IConstWorkUnit> wu = getWorkunit(ctx, wuid);
  553. if (wu)
  554. {
  555. Owned<IPropertyTreeIterator> sourceFiles = &wu->getFilesReadIterator();
  556. ForEach(*sourceFiles)
  557. {
  558. IPropertyTree &item = sourceFiles->query();
  559. varAppendMax(mb, 256, item, "@name");
  560. varAppendMax(mb, 64, item, "@cluster");
  561. mb.append(item.getPropBool("@super"));
  562. mb.append((unsigned) item.getPropInt("@useCount"));
  563. }
  564. }
  565. __lenResult = mb.length();
  566. __result = mb.detach();
  567. }
  568. WORKUNITSERVICES_API void wsWorkunitFilesWritten( ICodeContext *ctx, size32_t & __lenResult, void * & __result, const char *wuid )
  569. {
  570. MemoryBuffer mb;
  571. Owned<IConstWorkUnit> wu = getWorkunit(ctx, wuid);
  572. if (wu)
  573. {
  574. Owned<IPropertyTreeIterator> sourceFiles = &wu->getFileIterator();
  575. ForEach(*sourceFiles)
  576. {
  577. IPropertyTree &item = sourceFiles->query();
  578. varAppendMax(mb, 256, item, "@name");
  579. fixedAppend(mb, 10, item, "@graph");
  580. varAppendMax(mb, 64, item, "@cluster");
  581. mb.append( (unsigned) item.getPropInt("@kind"));
  582. }
  583. }
  584. __lenResult = mb.length();
  585. __result = mb.detach();
  586. }
  587. class WsTimingVisitor : public WuScopeVisitorBase
  588. {
  589. public:
  590. WsTimingVisitor(MemoryBuffer & _mb) : mb(_mb) {}
  591. virtual void noteStatistic(StatisticKind kind, unsigned __int64 value, IConstWUStatistic & cur) override
  592. {
  593. SCMStringBuffer desc;
  594. unsigned __int64 count = cur.getCount();
  595. unsigned __int64 max = cur.getMax();
  596. cur.getDescription(desc, true);
  597. mb.append((unsigned) count);
  598. mb.append((unsigned) (value / 1000000));
  599. mb.append((unsigned) max);
  600. varAppend(mb, desc.str());
  601. }
  602. protected:
  603. MemoryBuffer & mb;
  604. };
  605. WORKUNITSERVICES_API void wsWorkunitTimings( ICodeContext *ctx, size32_t & __lenResult, void * & __result, const char *wuid )
  606. {
  607. Owned<IConstWorkUnit> wu = getWorkunit(ctx, wuid);
  608. MemoryBuffer mb;
  609. if (wu)
  610. {
  611. WsTimingVisitor visitor(mb);
  612. WuScopeFilter filter("measure[Time],source[global]");
  613. Owned<IConstWUScopeIterator> iter = &wu->getScopeIterator(filter);
  614. ForEach(*iter)
  615. {
  616. iter->playProperties(visitor);
  617. }
  618. }
  619. __lenResult = mb.length();
  620. __result = mb.detach();
  621. }
  622. //This function is deprecated and no longer supported - I'm not sure it ever worked
  623. WORKUNITSERVICES_API IRowStream * wsWorkunitStatistics( ICodeContext *ctx, IEngineRowAllocator * allocator, const char *wuid, bool includeActivities, const char * filterText)
  624. {
  625. return createNullRowStream();
  626. }
  627. class WsStreamedStatistics : public CInterfaceOf<IRowStream>
  628. {
  629. public:
  630. WsStreamedStatistics(IConstWorkUnit * _wu, IEngineRowAllocator * _resultAllocator, const char * _filter)
  631. : wu(_wu), resultAllocator(_resultAllocator), filter(_filter)
  632. {
  633. iter.setown(&wu->getScopeIterator(filter));
  634. }
  635. virtual const void *nextRow()
  636. {
  637. return NULL;
  638. }
  639. virtual void stop()
  640. {
  641. iter.clear();
  642. }
  643. protected:
  644. Linked<IConstWorkUnit> wu;
  645. Linked<IEngineRowAllocator> resultAllocator;
  646. WuScopeFilter filter;
  647. Linked<IConstWUScopeIterator> iter;
  648. };
  649. WORKUNITSERVICES_API IRowStream * wsNewWorkunitStatistics( ICodeContext *ctx, IEngineRowAllocator * allocator, const char *wuid, const char * filterText)
  650. {
  651. Owned<IConstWorkUnit> wu = getWorkunit(ctx, wuid);
  652. if (!wu)
  653. return createNullRowStream();
  654. return new WsStreamedStatistics(wu, allocator, filterText);
  655. }
  656. WORKUNITSERVICES_API bool wsSetWorkunitAppValue( ICodeContext *ctx, const char *appname, const char *key, const char *value, bool overwrite)
  657. {
  658. if (appname && *appname && key && *key && value && *value)
  659. {
  660. WorkunitUpdate w(ctx->updateWorkUnit());
  661. w->setApplicationValue(appname, key, value, overwrite);
  662. return true;
  663. }
  664. return false;
  665. }
  666. WORKUNITSERVICES_API void setPluginContext(IPluginContext * _ctx) { parentCtx = _ctx; }
  667. WORKUNITSERVICES_API char * WORKUNITSERVICES_CALL fsGetBuildInfo(void)
  668. {
  669. return CTXSTRDUP(parentCtx, WORKUNITSERVICES_VERSION);
  670. }