runtime.c 27 KB

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