kaem.c 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  1. /* Copyright (C) 2016 Jeremiah Orians
  2. * This file is part of mescc-tools.
  3. *
  4. * mescc-tools is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation, either version 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * mescc-tools is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with mescc-tools. If not, see <http://www.gnu.org/licenses/>.
  16. */
  17. #include <stdlib.h>
  18. #include <stdio.h>
  19. #include <unistd.h>
  20. #include <sys/wait.h>
  21. #define FALSE 0
  22. //CONSTANT FALSE 0
  23. #define TRUE 1
  24. //CONSTANT TRUE 1
  25. #define max_string 4096
  26. //CONSTANT max_string 4096
  27. #define max_args 256
  28. //CONSTANT max_args 256
  29. char* int2str(int x, int base, int signed_p);
  30. int match(char* a, char* b);
  31. char** tokens;
  32. int command_done;
  33. int VERBOSE;
  34. int STRICT;
  35. int envp_length;
  36. /* Function for purging line comments */
  37. void collect_comment(FILE* input)
  38. {
  39. int c;
  40. do
  41. {
  42. c = fgetc(input);
  43. if(-1 == c)
  44. {
  45. fputs("IMPROPERLY TERMINATED LINE COMMENT!\nABORTING HARD\n", stderr);
  46. exit(EXIT_FAILURE);
  47. }
  48. } while('\n' != c);
  49. }
  50. /* Function for collecting RAW strings and removing the " that goes with them */
  51. int collect_string(FILE* input, int index, char* target)
  52. {
  53. int c;
  54. do
  55. {
  56. c = fgetc(input);
  57. if(-1 == c)
  58. { /* We never should hit EOF while collecting a RAW string */
  59. fputs("IMPROPERLY TERMINATED RAW string!\nABORTING HARD\n", stderr);
  60. exit(EXIT_FAILURE);
  61. }
  62. else if('"' == c)
  63. { /* Made it to the end */
  64. c = 0;
  65. }
  66. target[index] = c;
  67. index = index + 1;
  68. } while(0 != c);
  69. return index;
  70. }
  71. /* Function to collect an individual argument or purge a comment */
  72. char* collect_token(FILE* input)
  73. {
  74. char* token = calloc(max_string, sizeof(char));
  75. char c;
  76. int i = 0;
  77. do
  78. {
  79. c = fgetc(input);
  80. if(-1 == c)
  81. { /* Deal with end of file */
  82. fputs("execution complete\n", stderr);
  83. exit(EXIT_SUCCESS);
  84. }
  85. else if((' ' == c) || ('\t' == c))
  86. { /* space and tab are token seperators */
  87. c = 0;
  88. }
  89. else if('\n' == c)
  90. { /* Command terminates at end of line */
  91. c = 0;
  92. command_done = 1;
  93. }
  94. else if('"' == c)
  95. { /* RAW strings are everything between a pair of "" */
  96. i = collect_string(input, i, token);
  97. c = 0;
  98. }
  99. else if('#' == c)
  100. { /* Line comments to aid the humans */
  101. collect_comment(input);
  102. c = 0;
  103. command_done = 1;
  104. }
  105. else if('\\' == c)
  106. { /* Support for end of line escapes, drops the char after */
  107. fgetc(input);
  108. c = 0;
  109. }
  110. token[i] = c;
  111. i = i + 1;
  112. } while (0 != c);
  113. if(1 == i)
  114. { /* Nothing worth returning */
  115. free(token);
  116. return NULL;
  117. }
  118. return token;
  119. }
  120. char* copy_string(char* target, char* source)
  121. {
  122. while(0 != source[0])
  123. {
  124. target[0] = source[0];
  125. target = target + 1;
  126. source = source + 1;
  127. }
  128. return target;
  129. }
  130. int string_length(char* a)
  131. {
  132. int i = 0;
  133. while(0 != a[i]) i = i + 1;
  134. return i;
  135. }
  136. char* prepend_string(char* add, char* base)
  137. {
  138. char* ret = calloc(max_string, sizeof(char));
  139. copy_string(copy_string(ret, add), base);
  140. return ret;
  141. }
  142. char* find_char(char* string, char a)
  143. {
  144. if(0 == string[0]) return NULL;
  145. while(a != string[0])
  146. {
  147. string = string + 1;
  148. if(0 == string[0]) return string;
  149. }
  150. return string;
  151. }
  152. char* prematch(char* search, char* field)
  153. {
  154. do
  155. {
  156. if(search[0] != field[0]) return NULL;
  157. search = search + 1;
  158. field = field + 1;
  159. } while(0 != search[0]);
  160. return field;
  161. }
  162. char* env_lookup(char* token, char** envp)
  163. {
  164. if(NULL == envp) return NULL;
  165. int i = 0;
  166. char* ret = NULL;
  167. do
  168. {
  169. ret = prematch(token, envp[i]);
  170. if(NULL != ret) return ret;
  171. i = i + 1;
  172. } while(NULL != envp[i]);
  173. return NULL;
  174. }
  175. char* find_executable(char* name, char* PATH)
  176. {
  177. if(('.' == name[0]) || ('/' == name[0]))
  178. { /* assume names that start with . or / are relative or absolute */
  179. return name;
  180. }
  181. char* next = find_char(PATH, ':');
  182. char* trial;
  183. FILE* t;
  184. while(NULL != next)
  185. {
  186. next[0] = 0;
  187. trial = prepend_string(PATH, prepend_string("/", name));
  188. t = fopen(trial, "r");
  189. if(NULL != t)
  190. {
  191. fclose(t);
  192. return trial;
  193. }
  194. PATH = next + 1;
  195. next = find_char(PATH, ':');
  196. free(trial);
  197. }
  198. return NULL;
  199. }
  200. /* Function to check if the token is an envar and if it is get the pos of = */
  201. int check_envar(char* token)
  202. {
  203. int j;
  204. int equal_found;
  205. equal_found = 0;
  206. int found;
  207. char c;
  208. for(j = 0; j < string_length(token); j = j + 1)
  209. {
  210. if(token[j] == '=')
  211. { /* After = can be anything */
  212. equal_found = 1;
  213. break;
  214. }
  215. else
  216. { /* Should be A-z */
  217. found = 0;
  218. /* Represented numerically; 0 = 48 through 9 = 57 */
  219. for(c = 48; c <= 57; c = c + 1)
  220. {
  221. if(token[j] == c)
  222. {
  223. found = 1;
  224. }
  225. }
  226. /* Represented numerically; A = 65 through z = 122 */
  227. for(c = 65; c <= 122; c = c + 1)
  228. {
  229. if(token[j] == c)
  230. {
  231. found = 1;
  232. }
  233. }
  234. if(found == 0)
  235. { /* In all likelihood this isn't actually an environment variable */
  236. return 1;
  237. }
  238. }
  239. }
  240. if(equal_found == 0)
  241. { /* Not an envar */
  242. return 1;
  243. }
  244. return 0;
  245. }
  246. /* Function for executing our programs with desired arguments */
  247. void execute_commands(FILE* script, char** envp, int envp_length)
  248. {
  249. char* PATH;
  250. char* USERNAME;
  251. int i;
  252. int status;
  253. char* result;
  254. int j;
  255. int is_envar;
  256. char* program;
  257. int f;
  258. while(1)
  259. {
  260. tokens = calloc(max_args, sizeof(char*));
  261. PATH = env_lookup("PATH=", envp);
  262. if(NULL != PATH)
  263. {
  264. PATH = calloc(max_string, sizeof(char));
  265. copy_string(PATH, env_lookup("PATH=", envp));
  266. }
  267. USERNAME = env_lookup("LOGNAME=", envp);
  268. if((NULL == PATH) && (NULL == USERNAME))
  269. {
  270. PATH = calloc(max_string, sizeof(char));
  271. copy_string(PATH, "/root/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin");
  272. }
  273. else if(NULL == PATH)
  274. {
  275. PATH = prepend_string("/home/", prepend_string(USERNAME,"/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games"));
  276. }
  277. i = 0;
  278. status = 0;
  279. command_done = 0;
  280. do
  281. {
  282. result = collect_token(script);
  283. if(0 != result)
  284. { /* Not a comment string but an actual argument */
  285. tokens[i] = result;
  286. i = i + 1;
  287. }
  288. } while(0 == command_done);
  289. if(VERBOSE && (0 < i))
  290. {
  291. fputs(" +> ", stdout);
  292. for(j = 0; j < i; j = j + 1)
  293. {
  294. fputs(tokens[j], stdout);
  295. fputc(' ', stdout);
  296. }
  297. fputs("\n", stdout);
  298. }
  299. if(0 < i)
  300. { /* Not a line comment */
  301. is_envar = 0;
  302. if(check_envar(tokens[0]) == 0)
  303. { /* It's an envar! */
  304. is_envar = 1;
  305. envp[envp_length] = tokens[0]; /* Since arrays are 0 indexed */
  306. envp_length = envp_length + 1;
  307. }
  308. if(is_envar == 0)
  309. { /* Stuff to exec */
  310. program = find_executable(tokens[0], PATH);
  311. if(NULL == program)
  312. {
  313. fputs(tokens[0], stderr);
  314. fputs("Some weird shit went down with: ", stderr);
  315. fputs("\n", stderr);
  316. exit(EXIT_FAILURE);
  317. }
  318. f = fork();
  319. if (f == -1)
  320. {
  321. fputs("fork() failure", stderr);
  322. exit(EXIT_FAILURE);
  323. }
  324. else if (f == 0)
  325. { /* child */
  326. /* execve() returns only on error */
  327. execve(program, tokens, envp);
  328. /* Prevent infinite loops */
  329. _exit(EXIT_SUCCESS);
  330. }
  331. /* Otherwise we are the parent */
  332. /* And we should wait for it to complete */
  333. waitpid(f, &status, 0);
  334. if(STRICT && (0 != status))
  335. { /* Clearly the script hit an issue that should never have happened */
  336. fputs("Subprocess error ", stderr);
  337. fputs(int2str(status,10, FALSE), stderr);
  338. fputs("\nABORTING HARD\n", stderr);
  339. /* stop to prevent damage */
  340. exit(EXIT_FAILURE);
  341. }
  342. }
  343. /* Then go again */
  344. }
  345. }
  346. }
  347. int main(int argc, char** argv, char** envp)
  348. {
  349. VERBOSE = FALSE;
  350. STRICT = FALSE;
  351. char* filename = "kaem.run";
  352. FILE* script = NULL;
  353. /* Get envp_length */
  354. envp_length = 1;
  355. while(envp[envp_length] != NULL)
  356. {
  357. envp_length = envp_length + 1;
  358. }
  359. char** nenvp = calloc(envp_length + max_args + 1, sizeof(char*));
  360. int i;
  361. for(i = 0; i < envp_length; i = i + 1)
  362. {
  363. nenvp[i] = envp[i];
  364. }
  365. for(i = envp_length; i < (envp_length + max_args); i = i + 1)
  366. {
  367. nenvp[i] = "";
  368. }
  369. i = 1;
  370. while(i <= argc)
  371. {
  372. if(NULL == argv[i])
  373. {
  374. i = i + 1;
  375. }
  376. else if(match(argv[i], "-h") || match(argv[i], "--help"))
  377. {
  378. fputs("kaem only accepts --help, --version, --file, --verbose, --nightmare-mode or no arguments\n", stdout);
  379. exit(EXIT_SUCCESS);
  380. }
  381. else if(match(argv[i], "-f") || match(argv[i], "--file"))
  382. {
  383. filename = argv[i + 1];
  384. i = i + 2;
  385. }
  386. else if(match(argv[i], "n") || match(argv[i], "--nightmare-mode"))
  387. {
  388. fputs("Begin nightmare", stdout);
  389. envp = NULL;
  390. i = i + 1;
  391. }
  392. else if(match(argv[i], "-V") || match(argv[i], "--version"))
  393. {
  394. fputs("kaem version 0.6.0\n", stdout);
  395. exit(EXIT_SUCCESS);
  396. }
  397. else if(match(argv[i], "--verbose"))
  398. {
  399. VERBOSE = TRUE;
  400. i = i + 1;
  401. }
  402. else if(match(argv[i], "--strict"))
  403. {
  404. STRICT = TRUE;
  405. i = i + 1;
  406. }
  407. else
  408. {
  409. fputs("UNKNOWN ARGUMENT\n", stdout);
  410. exit(EXIT_FAILURE);
  411. }
  412. }
  413. script = fopen(filename, "r");
  414. if(NULL == script)
  415. {
  416. fputs("The file: ", stderr);
  417. fputs(filename, stderr);
  418. fputs(" can not be opened!\n", stderr);
  419. exit(EXIT_FAILURE);
  420. }
  421. execute_commands(script, nenvp, envp_length);
  422. fclose(script);
  423. return EXIT_SUCCESS;
  424. }