runtime.c 27 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include "unistd.h"
  4. #include <time.h>
  5. #include "runtime.h"
  6. #include "config.h"
  7. #include <curl/curl.h>
  8. #include "messageQueue.h"
  9. #include "serialPort.h"
  10. //popen
  11. #include <string.h>
  12. #include <errno.h>
  13. //#include <libexplain/popen.h>
  14. //alarm nonblocking loop/onetime/stop
  15. #include <pthread.h>
  16. struct ThreadInf aTi[MAX_NUMBER_OF_THREADS];
  17. int curRun = 0; //currently running
  18. int tIndex[MAX_NUMBER_OF_THREADS];
  19. //queue for callbacks
  20. #include <sys/queue.h>
  21. struct callbacks {
  22. TAILQ_ENTRY(callbacks) cbq;
  23. char * callbackStr;
  24. };
  25. struct callbacksq q;
  26. TAILQ_HEAD(callbacksq, callbacks);
  27. void initRuntime() {
  28. int status = 0;
  29. time_t t;
  30. /* Init random number generator */
  31. srand((unsigned) time(&t));
  32. /* Init Duktape */
  33. ctx = duk_create_heap_default();
  34. /* Init Module Loader */
  35. // TODO: implement module search function
  36. duk_module_duktape_init(ctx);
  37. // duk_get_global_string(ctx, "Duktape");
  38. // duk_push_c_function(ctx, native_mod_search, 4 /*nargs*/);
  39. // duk_put_prop_string(ctx, -2, "modSearch");
  40. /* Init System Functions */
  41. duk_push_c_function(ctx, native_print, 1 /*nargs*/);
  42. duk_put_global_string(ctx, "print");
  43. duk_push_c_function(ctx, native_sleep, 1 /*nargs*/);
  44. duk_put_global_string(ctx, "sleep");
  45. duk_push_c_function(ctx, native_writeFile, 3 /*nargs*/);
  46. duk_put_global_string(ctx, "writeFile");
  47. duk_push_c_function(ctx, native_readFile, 3 /*nargs*/);
  48. duk_put_global_string(ctx, "readFile");
  49. duk_push_c_function(ctx, native_setGpio, 2 /*nargs*/);
  50. duk_put_global_string(ctx, "setGpio");
  51. duk_push_c_function(ctx, native_playMp3, 1 /*nargs*/);
  52. duk_put_global_string(ctx, "playMP3");
  53. duk_push_c_function(ctx, native_systemCall, 1 /*nargs*/);
  54. duk_put_global_string(ctx, "system");
  55. duk_push_c_function(ctx, native_systemPopen, 1 /*nargs*/);
  56. duk_put_global_string(ctx, "popen");
  57. duk_push_c_function(ctx, native_launchProcess, 1 /*nargs*/);
  58. duk_put_global_string(ctx, "launchProcess");
  59. duk_push_c_function(ctx, native_killProcess, 1 /*nargs*/);
  60. duk_put_global_string(ctx, "killProcess");
  61. duk_push_c_function(ctx, native_checkProcess, 1 /*nargs*/);
  62. duk_put_global_string(ctx, "checkProcess");
  63. /* Init Card Functions */
  64. duk_push_c_function(ctx, native_initCard, 2 /*nargs*/);
  65. duk_put_global_string(ctx, "initCard");
  66. duk_push_c_function(ctx, native_updateCard, 1 /*nargs*/);
  67. duk_put_global_string(ctx, "updateCard");
  68. duk_push_c_function(ctx, native_deleteCard, 1 /*nargs*/);
  69. duk_put_global_string(ctx, "deleteCard");
  70. /* Init message queue functions */
  71. duk_push_c_function(ctx, native_mqConnect, 7 /*nargs*/);
  72. duk_put_global_string(ctx, "connect");
  73. duk_push_c_function(ctx, native_subscribe, 3 /*nargs*/);
  74. duk_put_global_string(ctx, "subscribe");
  75. duk_push_c_function(ctx, native_publish, 3 /*nargs*/);
  76. duk_put_global_string(ctx, "publish");
  77. /* Init Network Functions */
  78. duk_push_c_function(ctx, native_httpRequest, 1 /*nargs*/);
  79. /*
  80. options :
  81. method
  82. url
  83. header
  84. body
  85. */
  86. duk_put_global_string(ctx, "httpRequest");
  87. /* Init Serial Port Functions */
  88. duk_push_c_function(ctx, native_openSerialPort, 2 /*nargs*/);
  89. duk_put_global_string(ctx, "openSerialPort");
  90. duk_push_c_function(ctx, native_serialWrite, 1 /*nargs*/);
  91. duk_put_global_string(ctx, "serialWrite");
  92. duk_push_c_function(ctx, native_serialWriteByteArray, 1 /*nargs*/);
  93. duk_put_global_string(ctx, "serialWriteByteArray");
  94. duk_push_c_function(ctx, native_serialRead, 0 /*nargs*/);
  95. duk_put_global_string(ctx, "serialRead");
  96. duk_push_c_function(ctx, native_serialReadByteArray, 0 /*nargs*/);
  97. duk_put_global_string(ctx, "serialReadByteArray");
  98. /* Init Hardware Functions */
  99. duk_push_c_function(ctx, native_setButton, 2 /*nargs*/);
  100. duk_put_global_string(ctx, "setButton");
  101. duk_push_c_function(ctx, native_setNightLight, 1 /*nargs*/);
  102. duk_put_global_string(ctx, "setNightLight");
  103. /* Oboo software functions */
  104. duk_push_c_function(ctx, native_updateSystemTime, 1 /*nargs*/);
  105. duk_put_global_string(ctx, "updateSystemTime");
  106. /* initialize the message queue */
  107. initMessageQueue();
  108. /* initialize curl */
  109. curl_global_init(CURL_GLOBAL_SSL);
  110. for(int i=0; i != MAX_NUMBER_OF_THREADS; i++){
  111. tIndex[i] = 0; //0 not used, 1 used
  112. }
  113. TAILQ_INIT(&q);
  114. }
  115. void destroyRuntime () {
  116. /* duktape release */
  117. duk_destroy_heap(ctx);
  118. /* message queue release */
  119. destroyMessageQueue();
  120. /* curl clean up */
  121. curl_global_cleanup();
  122. }
  123. void printHelp () {
  124. printf("Oboo Card Runtime.\n\n");
  125. printf("Usage:\n");
  126. printf("\t./runtime <javascript file>\n");
  127. printf("\n");
  128. }
  129. // find path of binary and copy it to the path argument
  130. void getExeDir (char *path) {
  131. int numChars;
  132. char buf[MAX_CHAR_LEN];
  133. numChars = readlink("/proc/self/exe", buf, MAX_CHAR_LEN);
  134. buf[numChars] = 0; // null-terminate the string
  135. duk_push_string(ctx, buf);
  136. duk_put_global_string(ctx, "progName");
  137. duk_push_string(ctx, "\\/[^\\/]*$");
  138. duk_put_global_string(ctx, "reg");
  139. duk_eval_string(ctx,"var re = new RegExp (reg, ''); progName = progName.replace(re, '');");
  140. duk_get_global_string(ctx, "progName");
  141. strcpy(path, duk_get_string(ctx, -1));
  142. #ifdef MAC_DEBUG
  143. // DEBUG: for testing on mac
  144. strcpy(path, "./");
  145. #endif
  146. }
  147. int loadJS (char* filename, char* path) {
  148. duk_int_t rc;
  149. char filePath[MAX_CHAR_LEN];
  150. char lineBuff[MAX_CHAR_LEN];
  151. if (filename[0] == '/') {
  152. printf("filename is absolute!\n");
  153. // filename path is absolute
  154. sprintf(filePath, "%s", filename);
  155. } else {
  156. sprintf(filePath, "%s/%s", path, filename);
  157. }
  158. FILE *f = fopen(filePath, FILE_RD_FLAGS);
  159. if ((long int)f <= 0) {
  160. printf("ERROR: cannot open file '%s'\n", filePath);
  161. return -1;
  162. } else {
  163. duk_eval_string(ctx, "strBuff = ''");
  164. while (fgets(lineBuff, sizeof(lineBuff), f) != NULL) {
  165. duk_push_string(ctx, lineBuff);
  166. duk_put_global_string(ctx, "lineBuff");
  167. duk_eval_string(ctx, "strBuff += lineBuff;");
  168. }
  169. rc = duk_peval_string(ctx, "eval(strBuff);");
  170. if (rc != 0) {
  171. printf("error loading js file '%s': %s\n", filename, duk_safe_to_string(ctx, -1));
  172. }
  173. // close the file
  174. fclose(f);
  175. }
  176. return 0; /* no return value (= undefined) */
  177. }
  178. void runSetup () {
  179. duk_int_t rc;
  180. rc = duk_peval_string(ctx, "setup();");
  181. if (rc != 0) {
  182. printf("error running '%s' function: %s\n", "setup()", duk_safe_to_string(ctx, -1));
  183. }
  184. // TODO: change this to return EXIT_SUCCESS or EXIT_FAILURE
  185. }
  186. int runLoop () {
  187. duk_int_t rc;
  188. struct callbacks *p;
  189. TAILQ_FOREACH(p, &q, cbq) {
  190. // get the callback
  191. duk_get_global_string(ctx, p->callbackStr);
  192. // check to make sure it exists and that it's a function
  193. if (!duk_is_function(ctx, -1)) {
  194. printf("No callback registered\n");
  195. //return;
  196. }else{
  197. // remove the callback
  198. duk_push_null(ctx);
  199. duk_put_global_string(ctx, p->callbackStr);
  200. // protected call
  201. rc = duk_pcall(ctx, 0);
  202. if (rc != 0) {
  203. printf("Callback '%s' failed: '%s'\n", p->callbackStr, duk_safe_to_string(ctx, -1));
  204. }
  205. duk_pop(ctx);
  206. }
  207. }
  208. while (!TAILQ_EMPTY(&q)) {
  209. p = TAILQ_FIRST(&q);
  210. TAILQ_REMOVE(&q, p, cbq);
  211. free(p->callbackStr);
  212. free(p);
  213. }
  214. rc = duk_peval_string(ctx, "loop();");
  215. if (rc != 0) {
  216. printf("error running '%s' function: %s\n", "loop()", duk_safe_to_string(ctx, -1));
  217. }
  218. else {
  219. // check for exit condition
  220. if (duk_is_number(ctx, -1) && duk_to_int(ctx, -1) == JS_EXIT_CONDITION) {
  221. return EXIT_FAILURE;
  222. }
  223. }
  224. duk_pop(ctx);
  225. return EXIT_SUCCESS;
  226. }
  227. void handleMessage(char* topic, char* payload) {
  228. duk_int_t rc;
  229. duk_idx_t obj_idx;
  230. duk_get_global_string(ctx, "onRecvMessage");
  231. obj_idx = duk_push_object(ctx);
  232. duk_push_string(ctx, topic);
  233. duk_put_prop_string(ctx, obj_idx, "topic");
  234. duk_push_string(ctx, payload);
  235. duk_put_prop_string(ctx, obj_idx, "payload");
  236. rc = duk_pcall(ctx, 1);
  237. if (rc != 0) {
  238. printf("error running '%s' function: %s\n", "onRecvMessage()", duk_safe_to_string(ctx, -1));
  239. }
  240. }
  241. void invokeCallback(char* cbIndex) {
  242. duk_int_t rc;
  243. char buf[MAX_CHAR_LEN];
  244. sprintf(buf, "%s_%s", CB_PREFIX, cbIndex);
  245. printf("sizeof calloc %d\n", 7+sizeof(cbIndex)*sizeof(char));
  246. char * buff = malloc(7+sizeof(cbIndex)*sizeof(char));
  247. memset(buff, 0, 7+sizeof(cbIndex)*sizeof(char));
  248. sprintf(buff, "%s_%s\0", CB_PREFIX, cbIndex);
  249. struct callbacks* cbFs = malloc(sizeof(struct callbacks));//callbacks(buff);
  250. memset(cbFs, 0, sizeof(struct callbacks));
  251. cbFs->callbackStr=buff;
  252. if(TAILQ_EMPTY(&q)){
  253. TAILQ_INSERT_HEAD(&q, cbFs, cbq); //callback for save
  254. }else{
  255. TAILQ_INSERT_TAIL(&q, cbFs, cbq);
  256. }
  257. return;
  258. }
  259. duk_ret_t native_print(duk_context *ctx) {
  260. char *initString = (char*) duk_to_string(ctx, 0);
  261. //TODO: sendto MQTT (initString)
  262. printf("%s\n", initString);
  263. return 0; /* no return value (= undefined) */
  264. }
  265. duk_ret_t native_sleep(duk_context *ctx) {
  266. int sec = duk_to_int32(ctx, 0);
  267. sleep(sec);
  268. return 0; /* no return value (= undefined) */
  269. }
  270. duk_ret_t native_mod_search(duk_context *ctx) {
  271. /* Nargs was given as 4 and we get the following stack arguments:
  272. * index 0: id
  273. * index 1: require
  274. * index 2: exports
  275. * index 3: module
  276. */
  277. /* ... */
  278. printf("on native mod search id");
  279. return 0; /* no return value (= undefined) */
  280. }
  281. duk_ret_t native_initCard(duk_context *ctx) {
  282. if (duk_to_boolean(ctx, 1)) {
  283. // optional delay
  284. sleep(5);
  285. }
  286. printf("initCard: '%s'\n", duk_to_string(ctx, 0));
  287. sendMessage(MQ_LOCAL_CLIENT, MQ_CARD_TOPIC, duk_to_string(ctx, 0));
  288. return 0; /* no return value (= undefined) */
  289. }
  290. duk_ret_t native_updateCard(duk_context *ctx) {
  291. printf("updateCard: '%s'\n", duk_to_string(ctx, 0));
  292. sendMessage(MQ_LOCAL_CLIENT, MQ_CARD_TOPIC, duk_to_string(ctx, 0));
  293. return 0; /* no return value (= undefined) */
  294. }
  295. duk_ret_t native_deleteCard(duk_context *ctx) {
  296. char buf[64];
  297. int cardId = duk_to_int(ctx, 0);
  298. printf("deleteCard %d\n", cardId);
  299. sprintf(buf, "{\"cmd\":\"remove_card\",\"id\":%d}", cardId);
  300. sendMessage(MQ_LOCAL_CLIENT, MQ_CARD_TOPIC, buf);
  301. return 0; /* no return value (= undefined) */
  302. }
  303. // expected arguments:
  304. // 0 host
  305. // 1 port
  306. // 2 clientId (will be autogenerated if null)
  307. // 3 callback function
  308. // 4 enable TLS (secure) connection (bool)
  309. // 5 will topic (can be null)
  310. // 6 will payload (can be null)
  311. duk_ret_t native_mqConnect(duk_context *ctx) {
  312. int status;
  313. char buf[MAX_CHAR_LEN];
  314. char* clientId;
  315. char* willTopic = NULL;
  316. char* willPayload = NULL;
  317. // handle the clientId
  318. clientId = duk_to_string(ctx, 2);
  319. if (strcmp(clientId, JS_NULL) == 0 || clientId == NULL) {
  320. clientId = generateRandomString(CLIENT_ID_LEN);
  321. }
  322. // handle the will topic
  323. willTopic = duk_to_string(ctx, 5);
  324. if (strcmp(willTopic, JS_NULL) == 0 || willTopic == NULL) {
  325. willTopic = NULL;
  326. }
  327. // handle the will payload
  328. willPayload = duk_to_string(ctx, 6);
  329. if (strcmp(willPayload, JS_NULL) == 0 || willPayload == NULL) {
  330. willPayload = NULL;
  331. }
  332. // register the callback
  333. if (duk_is_function(ctx, 3)) {
  334. duk_dup(ctx, 3);
  335. sprintf(buf, "%s_%s", CB_PREFIX, clientId);
  336. duk_put_global_string(ctx, buf);
  337. }
  338. if (duk_to_boolean(ctx, 4)) {
  339. printf("establishing secure connection\n");
  340. status = connectMessageQueueSecure(MQ_REMOTE_CLIENT, duk_to_string(ctx, 0), duk_to_int(ctx, 1), clientId, willTopic, willPayload);
  341. }
  342. else {
  343. // make the C function call
  344. // printf("connect to %s:%d as '%s'\n", duk_to_string(ctx, 0), duk_to_int(ctx, 1), clientId);
  345. status = connectMessageQueue(MQ_LOCAL_CLIENT, duk_to_string(ctx, 0), duk_to_int(ctx, 1), clientId, willTopic, willPayload);
  346. }
  347. duk_push_int(ctx, status);
  348. return 1; /* 1 = return value at top */
  349. }
  350. duk_ret_t native_subscribe(duk_context *ctx) {
  351. int clientType = MQ_LOCAL_CLIENT;
  352. int *idPtr = malloc(sizeof(int));
  353. char buf[MAX_CHAR_LEN];
  354. printf("subscribe: '%s'\n", duk_to_string(ctx, 0));
  355. // check for optional boolean argument
  356. if (duk_to_boolean(ctx, 2)) {
  357. clientType = MQ_REMOTE_CLIENT;
  358. }
  359. int status = messageQueueSubscribe(clientType, duk_to_string(ctx, 0), idPtr);
  360. // register the callback - if any
  361. if (duk_is_function(ctx, 1)) {
  362. duk_dup(ctx, 1);
  363. sprintf(buf, "%s_s%d", CB_PREFIX, *idPtr);
  364. duk_put_global_string(ctx, buf);
  365. }
  366. // return value
  367. duk_push_int(ctx, status);
  368. free(idPtr);
  369. return 1; /* 1 = return value at top */
  370. }
  371. duk_ret_t native_publish(duk_context *ctx) {
  372. int clientType = MQ_LOCAL_CLIENT;
  373. printf("publishing to topic '%s': '%s'\n", duk_to_string(ctx, 0), duk_to_string(ctx, 1));
  374. // check for optional boolean argument
  375. if (duk_to_boolean(ctx, 2)) {
  376. clientType = MQ_REMOTE_CLIENT;
  377. }
  378. int status = sendMessage(clientType, duk_to_string(ctx, 0), duk_to_string(ctx, 1));
  379. duk_push_int(ctx, status);
  380. return 1; /* 1 = return value at top */
  381. }
  382. // http requests
  383. size_t recvDataCallback(char *ptr, size_t size, size_t nmemb, void *userdata)
  384. {
  385. size_t realSize = size * nmemb;
  386. strcpy((char*)userdata, ptr);
  387. return realSize;
  388. }
  389. static size_t
  390. WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp)
  391. {
  392. size_t realsize = size * nmemb;
  393. struct MemoryStruct *mem = (struct MemoryStruct *)userp;
  394. mem->memory = realloc(mem->memory, mem->size + realsize + 1);
  395. if(mem->memory == NULL) {
  396. /* out of memory! */
  397. printf("not enough memory (realloc returned NULL)\n");
  398. return 0;
  399. }
  400. memcpy(&(mem->memory[mem->size]), contents, realsize);
  401. mem->size += realsize;
  402. mem->memory[mem->size] = 0;
  403. return realsize;
  404. }
  405. int httpRequest (char* url, char* method, char* response) {
  406. CURL *curl;
  407. CURLcode res;
  408. struct MemoryStruct chunk;
  409. chunk.memory = malloc(1); /* will be grown as needed by the realloc above */
  410. chunk.size = 0; /* no data at this point */
  411. curl = curl_easy_init();
  412. if(curl) {
  413. curl_easy_setopt(curl, CURLOPT_URL, url);
  414. // TODO: wrap this with a check to make sure method == GET
  415. curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
  416. curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
  417. /* some servers don't like requests that are made without a user-agent field, so we provide one */
  418. curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0");
  419. res = curl_easy_perform(curl);
  420. if(res != CURLE_OK) {
  421. fprintf(stderr, "curl_easy_perform() failed: %s (%d)\n", curl_easy_strerror(res), res);
  422. curl_easy_cleanup(curl);
  423. return -1; /* return an error */
  424. } else {
  425. curl_easy_cleanup(curl);
  426. strcpy(response, chunk.memory);
  427. return 0; /* 1 = return value at top */
  428. }
  429. }
  430. return -1;
  431. }
  432. duk_ret_t native_httpRequest(duk_context *ctx) {
  433. int status;
  434. char data[8192];
  435. /* Get request options */
  436. duk_push_string(ctx, "url");
  437. duk_get_prop(ctx, 0);
  438. char * url = (char*) duk_to_string(ctx, -1);
  439. printf("url: %s\n", url);
  440. duk_push_string(ctx, "method");
  441. duk_get_prop(ctx, 0);
  442. char * method = (char*) duk_to_string(ctx, -1);
  443. printf("method: %s\n", method);
  444. // perform the http request
  445. status = httpRequest(url, method, data);
  446. if (status == 0) {
  447. duk_push_string(ctx, data);
  448. return 1; /* 1 = return value at top */
  449. }
  450. return 0; /* 0 = return 'undefined' */
  451. }
  452. // oboo hardware functions
  453. duk_ret_t native_setButton(duk_context *ctx) {
  454. // code review: potentially rework this so that the entire payload generated in js and only executed in c
  455. char buf[MAX_CHAR_LEN];
  456. printf("native setButton: %d: 0x%06x\n", duk_to_int(ctx, 0), duk_to_int(ctx, 1));
  457. sprintf(buf, "{\"cmd\":\"button\", \"id\":%d, \"value\":\"0x%06x\"}", duk_to_int(ctx, 0), duk_to_int(ctx, 1));
  458. int status = sendMessage(MQ_LOCAL_CLIENT, MQ_SET_BUTTON_COLOR_TOPIC, buf);
  459. return 0; /* no return value (= undefined) */
  460. }
  461. duk_ret_t native_setNightLight(duk_context *ctx) {
  462. // code review: potentially rework this so that the entire payload generated in js and only executed in c
  463. char buf[MAX_CHAR_LEN];
  464. printf("native setNightLight: 0x%06x\n", duk_to_int(ctx, 0));
  465. sprintf(buf, "{\"cmd\":\"nightlight\", \"value\":\"0x%06x\"}", duk_to_int(ctx, 0));
  466. int status = sendMessage(MQ_LOCAL_CLIENT, MQ_SET_NIGHTLIGHT_TOPIC, buf);
  467. return 0; /* no return value (= undefined) */
  468. }
  469. // oboo software functions
  470. duk_ret_t native_updateSystemTime(duk_context *ctx) {
  471. char buf[MAX_CHAR_LEN];
  472. // TODO: insert a check here to make sure input string from duktape is all numbers
  473. sprintf(buf, "sh /usr/bin/mcu/setSystemTime.sh %s\n", duk_to_string(ctx, 0));
  474. system(buf);
  475. return 0; /* no return value (= undefined) */
  476. }
  477. // serial port functions
  478. duk_ret_t native_openSerialPort(duk_context *ctx) {
  479. int baudrate = DEFAULT_BAUDRATE;
  480. if (duk_is_number(ctx, 1)) {
  481. baudrate = duk_to_int(ctx, 1);
  482. }
  483. int status = initSerialPort(duk_to_string(ctx, 0), baudrate);
  484. duk_push_int(ctx, status);
  485. return 1; /* 1 = return value at top */
  486. }
  487. duk_ret_t native_serialWrite(duk_context *ctx) {
  488. // printf("writing serial message '%s'\n", duk_to_string(ctx, 0));
  489. int status = writeSerialPort(duk_to_string(ctx, 0));
  490. // printf(" writeSerialPort returned %d\n", status);
  491. duk_push_int(ctx, status);
  492. // printf("pushed int, stack size is %ld\n", (long)duk_get_top_index(ctx));
  493. return 1; /* 1 = return value at top */
  494. }
  495. duk_ret_t native_serialWriteByteArray(duk_context *ctx) {
  496. unsigned char *buf;
  497. duk_size_t sz;
  498. int status = 0;
  499. buf = (unsigned char *) duk_get_buffer_data(ctx, 0, &sz);
  500. if (buf != NULL) {
  501. // printf("Found buffer of size %d:\n", (int)sz);
  502. for (int i = 0; i < (int)sz; i++) {
  503. // printf("%c (%d), ", buf[i], (int)buf[i]);
  504. writeCharSerialPort(buf[i]);
  505. }
  506. // printf("\n");
  507. }
  508. duk_push_int(ctx, status);
  509. // printf("pushed int, stack size is %ld\n", (long)duk_get_top_index(ctx));
  510. return 1; /* 1 = return value at top */
  511. }
  512. duk_ret_t native_serialRead(duk_context *ctx) {
  513. char buf[MAX_CHAR_LEN] = "";
  514. int status = readSerialPort(buf);
  515. duk_push_string(ctx, buf);
  516. return 1; /* 1 = return value at top */
  517. }
  518. duk_ret_t native_serialReadByteArray(duk_context *ctx) {
  519. char buf[MAX_CHAR_LEN] = "";
  520. int rdlen = readSerialPort(buf);
  521. // create an array to return to JS
  522. duk_eval_string(ctx, "retArray = [];");
  523. if (rdlen > 0) {
  524. for(int i = 0; i < rdlen; i++) {
  525. duk_push_int(ctx, (uint8_t)buf[i]);
  526. duk_put_global_string(ctx, "input");
  527. duk_eval_string(ctx, "retArray.push(input)");
  528. duk_pop(ctx); // need to remove the integer from the value stack
  529. }
  530. }
  531. // return the array (at top of value stack) to JS
  532. return 1; /* 1 = return value at top */
  533. }
  534. // filesystem operations
  535. duk_ret_t native_writeFile(duk_context *ctx) {
  536. int status = 0;
  537. char* filePath = (char*) duk_to_string(ctx, 0);
  538. char* content = (char*) duk_to_string(ctx, 1);
  539. // write the contents to the file
  540. FILE *f = fopen(filePath, FILE_WR_FLAGS);
  541. if ((long int)f <= 0) {
  542. printf("ERROR: cannot open file '%s'\n", filePath);
  543. status = -1;
  544. } else {
  545. // write contents to file
  546. status = fputs(content, f);
  547. if (status < 0) {
  548. printf("ERROR: writing to file '%s'\n", filePath);
  549. }
  550. // close the file
  551. fclose(f);
  552. }
  553. // execute the callback function
  554. if (duk_is_function(ctx, 2)) {
  555. duk_int_t rc;
  556. duk_dup(ctx, 2);
  557. // push cb err argument to stack
  558. // true if error, false if ok
  559. duk_push_boolean(ctx, (status < 0 ? 1 : 0));
  560. rc = duk_pcall(ctx, 1);
  561. if (rc != 0) {
  562. printf("writeFile callback error: %s\n", duk_safe_to_string(ctx, -1));
  563. }
  564. }
  565. // TODO: need a return statement here?
  566. }
  567. duk_ret_t native_readFile(duk_context *ctx) {
  568. int status = 0;
  569. char lineBuff[MAX_CHAR_LEN];
  570. char* filePath = (char*) duk_to_string(ctx, 0);
  571. char* encoding = (char*) duk_to_string(ctx, 1);
  572. duk_int_t rc;
  573. // read the file - need the callback though
  574. if (duk_is_function(ctx, 2)) {
  575. FILE *f = fopen(filePath, FILE_RD_FLAGS);
  576. if ((long int)f <= 0) {
  577. printf("ERROR: cannot open file '%s'\n", filePath);
  578. status = -1;
  579. } else {
  580. // read contents of the file
  581. duk_eval_string(ctx, "strBuff = String('')");
  582. while (fgets(lineBuff, sizeof(lineBuff), f) != NULL) {
  583. duk_push_string(ctx, lineBuff);
  584. duk_put_global_string(ctx, "lineBuff");
  585. duk_eval_string(ctx, "strBuff += String(lineBuff);");
  586. // code review: do we need to call duk_pop here? we're just pushing a bunch of strings to the stack
  587. }
  588. // close the file
  589. fclose(f);
  590. }
  591. duk_dup(ctx, 2);
  592. // push cb err argument to stack
  593. // true if error, false if ok
  594. duk_push_boolean(ctx, (status < 0 ? 1 : 0));
  595. // push cb data argument to stack
  596. if (status < 0 ) {
  597. duk_push_null(ctx);
  598. } else {
  599. duk_get_global_string(ctx, "strBuff");
  600. // duk_safe_to_string(ctx, -1);
  601. }
  602. rc = duk_pcall(ctx, 2);
  603. if (rc != 0) {
  604. printf("callback error: %s\n", duk_safe_to_string(ctx, -1));
  605. }
  606. // code review: potentially call duk_pop here?
  607. }
  608. // TODO: need a return statement here?
  609. }
  610. /// system operations
  611. // use gpioctl to set GPIO output value
  612. // arg0 - gpio number
  613. // arg1 - gpio value (0 or 1)
  614. duk_ret_t native_setGpio(duk_context *ctx) {
  615. char buf[MAX_CHAR_LEN];
  616. int gpio = duk_to_int(ctx, 0);
  617. int value = duk_to_int(ctx, 1);
  618. sprintf(buf, "gpioctl dirout-%s %d\n", (value == 1 ? "high" : "low"), gpio);
  619. #ifndef MAC_DEBUG
  620. system(buf);
  621. #else
  622. printf("%s", buf);
  623. #endif
  624. return 0; /* no return value (= undefined) */
  625. }
  626. // use mpg123 to play an MP3 file
  627. // arg0 - path to mp3 file
  628. duk_ret_t native_playMp3(duk_context *ctx) {
  629. char buf[MAX_CHAR_LEN];
  630. sprintf(buf, "mpg123 %s\n", duk_to_string(ctx, 0));
  631. #ifndef MAC_DEBUG
  632. system(buf);
  633. #else
  634. printf("%s", buf);
  635. #endif
  636. return 0; /* no return value (= undefined) */
  637. }
  638. // make a generic system call
  639. // arg0 - system call to run
  640. duk_ret_t native_systemCall(duk_context *ctx) {
  641. char buf[MAX_CHAR_LEN];
  642. sprintf(buf, "%s\n", duk_to_string(ctx, 0));
  643. #ifndef MAC_DEBUG
  644. system(buf);
  645. #else
  646. printf("%s", buf);
  647. #endif
  648. return 0; /* no return value (= undefined) */
  649. }
  650. duk_ret_t native_systemPopen(duk_context *ctx) {
  651. char buf[MAX_CHAR_LEN];
  652. sprintf(buf, "%s 2$>1\n", duk_to_string(ctx, 0));
  653. char data[MAX_DATA_SIZE]={0x0};
  654. char tmp[MAX_CHAR_LEN]={0x0};
  655. long total=0;
  656. int retval=0;
  657. FILE *in=NULL;
  658. sprintf(buf, "2>&1 %s \n", duk_to_string(ctx, 0));
  659. printf("%s", buf);
  660. in=popen(buf, "r");
  661. if(in==NULL)
  662. {
  663. sprintf(data, "%s\n", strerror(errno));
  664. //perror("Shell execution error");
  665. //sprintf(tmp, "%s\n", explain_popen(buf, "r"));
  666. //exit(EXIT_FAILURE);
  667. }
  668. while(fgets(tmp,4095,in)!=NULL)
  669. {
  670. total+=atol(tmp);
  671. strcat(data, tmp);
  672. }
  673. //printf("inside tmp %s len of tmp %d\n", tmp, strlen(tmp));
  674. if(!feof(in))
  675. {
  676. strcat(data, strerror(errno));
  677. //perror("Input stream error");
  678. //exit(EXIT_FAILURE);
  679. }
  680. // TODO: bugi: had this sleep in bugi branch, is it required?
  681. //sleep(10);
  682. retval=pclose(in);
  683. if(retval==EOF || retval==127)
  684. {
  685. perror("Shell invocation error");
  686. exit(EXIT_FAILURE);
  687. }
  688. printf("pclose return value: %d, perror %s\n ", WEXITSTATUS(retval), strerror(retval));
  689. //fprintf(stdout,"%-64s total: %d\n",buf,total);
  690. //duk_push_string(ctx, "vrati li");
  691. duk_push_string(ctx, data);
  692. return 1;
  693. }
  694. int getPID(char* processName, int *processID)
  695. {
  696. char* args[2];
  697. char str[100];
  698. int rtn;
  699. args[0] = "pidof ";
  700. char * process;
  701. process = strtok(processName, " ");
  702. if(process != NULL){
  703. args[1] = process;
  704. }else{
  705. //sprintf(processID, "%d", 0);
  706. *processID = 0;
  707. rtn = 0;
  708. return rtn;
  709. }
  710. strcpy (str,args[0]);
  711. strcat (str,args[1]);
  712. FILE* cmdresults = popen(str, "r");
  713. char data[MAX_DATA_SIZE]={0x0};
  714. char tmp[MAX_CHAR_LEN]={0x0};
  715. while(fgets(tmp,4095,cmdresults)!=NULL)
  716. {
  717. strcat(data, tmp);
  718. }
  719. //printf("Data %s", data);
  720. char * pch;
  721. pch = strtok (data," ");
  722. if(pch != NULL){ //first is allways new TODO: analyse
  723. rtn = strlen(pch);
  724. *processID = atoi(pch);
  725. }else { *processID = 0; }
  726. int retval=pclose(cmdresults);
  727. if(retval==EOF || retval==127)
  728. {
  729. perror("Shell invocation error");
  730. }
  731. int a = system(str);
  732. return rtn;
  733. }
  734. void *run_thread( void *ptr )
  735. {
  736. struct ThreadInf * thrInfo;
  737. char tmp[MAX_CHAR_LEN]={0x0};
  738. thrInfo = (struct ThreadInf *) ptr;
  739. while( thrInfo->run != 0 ){
  740. if(thrInfo->in == NULL){
  741. char start[MAX_CHAR_LEN] = {0x0};
  742. sprintf(start, "%s", thrInfo->command);
  743. thrInfo->in = popen(start, "r");
  744. sleep(1);
  745. getPID(start, &(thrInfo->pId));
  746. }else if(thrInfo->run == 1){
  747. // executes when the process exits
  748. sleep(1);
  749. if(fgets(tmp,4095,thrInfo->in)==NULL){
  750. int retval=pclose(thrInfo->in);
  751. if(retval==EOF || retval==127)
  752. {
  753. perror("Shell invocation error");
  754. }
  755. //printf("pclose return value: %d, perror %s\n ", WEXITSTATUS(retval), strerror(retval));
  756. thrInfo->run = 0;
  757. thrInfo->in = NULL;
  758. tIndex[thrInfo->index] = 0;
  759. thrInfo->index = 0;
  760. curRun--;
  761. }
  762. }
  763. }
  764. }
  765. duk_ret_t native_launchProcess(duk_context *ctx) {
  766. char data[MAX_DATA_SIZE]={0x0};
  767. char buf[MAX_CHAR_LEN] = {0x0};
  768. sprintf(buf, "%s", duk_to_string(ctx, 0));
  769. int i;
  770. if(curRun < MAX_NUMBER_OF_THREADS){
  771. for(i=0; i!= MAX_NUMBER_OF_THREADS; i++){
  772. if(tIndex[i] == 0) { tIndex[i] = 1; break; }
  773. }
  774. //new index
  775. strcpy(aTi[i].command, buf);
  776. aTi[i].in = NULL;
  777. int iret = pthread_create(&(aTi[i].thread), NULL, run_thread, (void *)&aTi[i]);
  778. if(iret)
  779. {
  780. sprintf(data, "Error - pthread_create() return code: %d\n", iret);
  781. }
  782. aTi[i].run = 1;
  783. aTi[i].index = i;
  784. curRun++;
  785. //printf("cnt of treads that are running %d\n", curRun);
  786. }else{
  787. printf("number of threads exceeded max number (%d) of separate processes allowed\n", MAX_NUMBER_OF_THREADS );
  788. duk_push_int(ctx, 0);
  789. return 1;
  790. }
  791. sleep(2); //needed so that we can return pid of command called
  792. if(i != 0 || (i == 0 && tIndex[i] == 1)) { duk_push_int(ctx, aTi[i].pId); }
  793. return 1;
  794. }
  795. duk_ret_t native_killProcess(duk_context *ctx) {
  796. char dat[MAX_DATA_SIZE]={0x0};
  797. char buff[MAX_CHAR_LEN] = {0x0};
  798. sprintf(buff, "kill -9 %s", duk_to_string(ctx, 0));
  799. int j = system(buff);
  800. if (j == 0) { duk_push_int(ctx, 1); }
  801. else { duk_push_int(ctx, 0); }
  802. return 1;
  803. }
  804. duk_ret_t native_checkProcess(duk_context *ctx) {
  805. char buff[MAX_CHAR_LEN] = {0x0};
  806. sprintf(buff, "ps -p %s | grep %s | awk '{print $1}'", duk_to_string(ctx, 0), duk_to_string(ctx, 0)); //ps -p 14 | grep 14 | awk '{print $1}'
  807. printf("cmd %s\n", buff);
  808. FILE* cmdresults = popen(buff, "r");
  809. char data[MAX_DATA_SIZE]={0x0};
  810. char tmp[MAX_CHAR_LEN]={0x0};
  811. while(fgets(tmp,4095,cmdresults)!=NULL)
  812. {
  813. }
  814. int retval=pclose(cmdresults);
  815. if(retval==EOF || retval==127)
  816. {
  817. perror("Shell invocation error");
  818. }
  819. sprintf(data, "%s", duk_to_string(ctx, 0));
  820. if(strstr(tmp, data)!=NULL){
  821. duk_push_int(ctx, 1);
  822. }else duk_push_int(ctx, 0);
  823. return 1;
  824. }