pipe.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594
  1. /*
  2. * pipe.c
  3. *
  4. * Copyright (C) 2015 Aleksandar Andrejevic <theflash@sdf.lonestar.org>
  5. *
  6. * This program is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU Affero General Public License as
  8. * published by the Free Software Foundation, either version 3 of the
  9. * License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU Affero General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Affero General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include <pipe.h>
  20. #include <exception.h>
  21. #include <timer.h>
  22. #include <process.h>
  23. #include <thread.h>
  24. #include <syscalls.h>
  25. #include <heap.h>
  26. static dword_t create_pipe_internal(pipe_t **_pipe, dword_t flags)
  27. {
  28. pipe_t *pipe = (pipe_t*)malloc(sizeof(pipe_t));
  29. if (pipe == NULL) return ERR_NOMEMORY;
  30. init_object(&pipe->header, NULL, OBJECT_PIPE);
  31. init_pipe(pipe, flags);
  32. dword_t ret = create_object(&pipe->header);
  33. if (ret != ERR_SUCCESS)
  34. {
  35. free(pipe);
  36. return ret;
  37. }
  38. *_pipe = pipe;
  39. return ERR_SUCCESS;
  40. }
  41. dword_t read_pipe(pipe_t *pipe, void *buffer, size_t *size, dword_t timeout)
  42. {
  43. dword_t ret = ERR_SUCCESS;
  44. size_t requested_size = *size;
  45. lock_acquire(&pipe->lock);
  46. if (pipe->flags & PIPE_MESSAGE)
  47. {
  48. if (pipe->fifo.next == &pipe->fifo)
  49. {
  50. lock_release(&pipe->lock);
  51. wait_condition_t condition = { .type = WAIT_UNTIL_NOT_EQUAL, .pointer = (dword_t*)&pipe->fifo.next, .value = 0 };
  52. wait_result_t status = scheduler_wait(&condition, timeout);
  53. if (status != ERR_SUCCESS) return (status == WAIT_TIMED_OUT) ? ERR_TIMEOUT : ERR_CANCELED;
  54. lock_acquire(&pipe->lock);
  55. }
  56. pipe_message_entry_t *entry = CONTAINER_OF(pipe->fifo.next, pipe_message_entry_t, link);
  57. *size = entry->size;
  58. if (requested_size < entry->size)
  59. {
  60. ret = ERR_SMALLBUF;
  61. goto cleanup;
  62. }
  63. memcpy(buffer, entry->data, entry->size);
  64. list_remove(&entry->link);
  65. heap_free(&evictable_heap, entry);
  66. }
  67. else
  68. {
  69. byte_t *data = (byte_t*)buffer;
  70. *size = 0;
  71. while (*size < requested_size)
  72. {
  73. if (pipe->fifo.next == &pipe->fifo)
  74. {
  75. lock_release(&pipe->lock);
  76. wait_condition_t condition = { .type = WAIT_UNTIL_NOT_EQUAL, .pointer = (dword_t*)&pipe->fifo.next, .value = 0 };
  77. wait_result_t status = scheduler_wait(&condition, timeout);
  78. if (status != ERR_SUCCESS) return (status == WAIT_TIMED_OUT) ? ERR_TIMEOUT : ERR_CANCELED;
  79. lock_acquire(&pipe->lock);
  80. }
  81. pipe_fifo_entry_t *entry = CONTAINER_OF(pipe->fifo.next, pipe_fifo_entry_t, link);
  82. while ((*size < requested_size) && ((entry->start != entry->end) || entry->full))
  83. {
  84. data[(*size)++] = entry->data[entry->start++];
  85. entry->start %= PIPE_BLOCK_SIZE;
  86. entry->full = FALSE;
  87. }
  88. if ((entry->start == entry->end) && !entry->full)
  89. {
  90. list_remove(&entry->link);
  91. heap_free(&evictable_heap, entry);
  92. }
  93. }
  94. }
  95. pipe->bytes_ready -= *size;
  96. cleanup:
  97. lock_release(&pipe->lock);
  98. return ret;
  99. }
  100. dword_t write_pipe(pipe_t *pipe, const void *buffer, size_t size)
  101. {
  102. dword_t ret = ERR_SUCCESS;
  103. dword_t count = 0;
  104. lock_acquire(&pipe->lock);
  105. if (pipe->flags & PIPE_MESSAGE)
  106. {
  107. pipe_message_entry_t *entry = heap_alloc(&evictable_heap, sizeof(pipe_message_entry_t) + size);
  108. if (!entry)
  109. {
  110. ret = ERR_NOMEMORY;
  111. goto cleanup;
  112. }
  113. entry->size = size;
  114. memcpy(entry->data, buffer, size);
  115. list_append(&pipe->fifo, &entry->link);
  116. count = size;
  117. }
  118. else
  119. {
  120. const byte_t *data = (const byte_t*)buffer;
  121. pipe_fifo_entry_t *entry = (pipe->fifo.prev != &pipe->fifo) ? (pipe_fifo_entry_t*)pipe->fifo.prev : NULL;
  122. while (count < size)
  123. {
  124. if (!entry || entry->full)
  125. {
  126. entry = (pipe_fifo_entry_t*)heap_alloc(&evictable_heap, sizeof(pipe_fifo_entry_t));
  127. if (!entry)
  128. {
  129. ret = ERR_NOMEMORY;
  130. break;
  131. }
  132. entry->start = entry->end = 0;
  133. entry->full = FALSE;
  134. list_append(&pipe->fifo, &entry->link);
  135. }
  136. while ((count < size) && (entry->start != entry->end))
  137. {
  138. entry->data[entry->end++] = data[count++];
  139. entry->end %= PIPE_BLOCK_SIZE;
  140. }
  141. if (entry->start == entry->end) entry->full = TRUE;
  142. }
  143. }
  144. pipe->bytes_ready += count;
  145. cleanup:
  146. lock_release(&pipe->lock);
  147. return ret;
  148. }
  149. void pipe_cleanup(object_t *obj)
  150. {
  151. pipe_t *pipe = (pipe_t*)obj;
  152. while (pipe->fifo.next != &pipe->fifo)
  153. {
  154. pipe_fifo_entry_t *entry = CONTAINER_OF(pipe->fifo.next, pipe_fifo_entry_t, link);
  155. list_remove(&entry->link);
  156. free(entry);
  157. }
  158. }
  159. dword_t pipe_pre_wait(object_t *obj, void *parameter, wait_condition_t *condition)
  160. {
  161. pipe_t *pipe = (pipe_t*)obj;
  162. dword_t min_bytes = parameter ? (dword_t)parameter : 1;
  163. condition->type = WAIT_UNTIL_NOT_LESS;
  164. condition->pointer = &pipe->bytes_ready;
  165. condition->value = min_bytes;
  166. return ERR_SUCCESS;
  167. }
  168. sysret_t syscall_create_pipeline(const char *name, dword_t flags, access_flags_t access, handle_t *handle)
  169. {
  170. dword_t ret;
  171. handle_t safe_handle;
  172. const char *safe_name = NULL;
  173. if (get_previous_mode() == USER_MODE)
  174. {
  175. dword_t name_length = 0;
  176. EH_TRY name_length = strlen(name);
  177. EH_CATCH EH_ESCAPE(return ERR_BADPTR);
  178. EH_DONE;
  179. if (!check_usermode(name, name_length + 1)) return ERR_BADPTR;
  180. if (!check_usermode(handle, sizeof(handle_t))) return ERR_BADPTR;
  181. safe_name = copy_user_string(name);
  182. if (safe_name == NULL) return ERR_BADPTR;
  183. }
  184. else
  185. {
  186. safe_name = (const char*)name;
  187. }
  188. pipeline_t *pipeline = (pipeline_t*)malloc(sizeof(pipeline_t));
  189. if (pipeline == NULL)
  190. {
  191. ret = ERR_NOMEMORY;
  192. goto cleanup;
  193. }
  194. init_object(&pipeline->header, safe_name, OBJECT_PIPELINE);
  195. pipeline->flags = flags;
  196. lock_init(&pipeline->lock);
  197. pipeline->status = PIPELINE_IDLE;
  198. ret = create_object(&pipeline->header);
  199. if (ret != ERR_SUCCESS)
  200. {
  201. free(pipeline->header.name);
  202. free(pipeline);
  203. goto cleanup;
  204. }
  205. ret = open_object(&pipeline->header, access, &safe_handle);
  206. if (ret != ERR_SUCCESS) goto cleanup;
  207. EH_TRY
  208. {
  209. *handle = safe_handle;
  210. }
  211. EH_CATCH
  212. {
  213. syscall_close_object(safe_handle);
  214. ret = ERR_BADPTR;
  215. }
  216. EH_DONE;
  217. cleanup:
  218. dereference(&pipeline->header);
  219. if (get_previous_mode() == USER_MODE) free((void*)safe_name);
  220. return ret;
  221. }
  222. sysret_t syscall_open_pipeline(const char *name, access_flags_t access, handle_t *handle)
  223. {
  224. dword_t ret;
  225. handle_t safe_handle;
  226. const char *safe_name = NULL;
  227. if (get_previous_mode() == USER_MODE)
  228. {
  229. dword_t name_length = 0;
  230. EH_TRY name_length = strlen(name);
  231. EH_CATCH EH_ESCAPE(return ERR_BADPTR);
  232. EH_DONE;
  233. if (!check_usermode(name, name_length + 1)) return ERR_BADPTR;
  234. if (!check_usermode(handle, sizeof(handle_t))) return ERR_BADPTR;
  235. safe_name = copy_user_string(name);
  236. if (safe_name == NULL) return ERR_BADPTR;
  237. }
  238. else
  239. {
  240. safe_name = (const char*)name;
  241. }
  242. ret = open_object_by_name(safe_name, OBJECT_PIPELINE, access, &safe_handle);
  243. if (ret != ERR_SUCCESS) goto cleanup;
  244. EH_TRY
  245. {
  246. *handle = safe_handle;
  247. }
  248. EH_CATCH
  249. {
  250. ret = ERR_BADPTR;
  251. }
  252. EH_DONE;
  253. cleanup:
  254. if (get_previous_mode() == USER_MODE) free((void*)safe_name);
  255. return ret;
  256. }
  257. static dword_t pipeline_create_pipes(pipeline_t *pipeline)
  258. {
  259. dword_t request_flags = (pipeline->flags & PIPELINE_REQUEST_MESSAGE) ? PIPE_MESSAGE : 0;
  260. dword_t response_flags = (pipeline->flags & PIPELINE_RESPONSE_MESSAGE) ? PIPE_MESSAGE : 0;
  261. dword_t ret = create_pipe_internal(&pipeline->request_pipe, request_flags);
  262. if (ret != ERR_SUCCESS) return ret;
  263. if (!(pipeline->flags & PIPELINE_REQUEST_ONLY))
  264. {
  265. ret = create_pipe_internal(&pipeline->response_pipe, response_flags);
  266. if (ret != ERR_SUCCESS)
  267. {
  268. dereference(&pipeline->request_pipe->header);
  269. pipeline->request_pipe = NULL;
  270. }
  271. }
  272. return ret;
  273. }
  274. static dword_t connect_to_pipeline(pipeline_t *pipeline, pipe_connection_t *conn)
  275. {
  276. lock_acquire_smart(&pipeline->lock);
  277. dword_t ret = open_object(&pipeline->request_pipe->header, 0, &conn->request_pipe);
  278. if (ret != ERR_SUCCESS) goto cleanup;
  279. if (pipeline->response_pipe)
  280. {
  281. ret = open_object(&pipeline->response_pipe->header, 0, &conn->response_pipe);
  282. if (ret != ERR_SUCCESS) syscall_close_object(conn->response_pipe);
  283. }
  284. cleanup:
  285. lock_release(&pipeline->lock);
  286. return ret;
  287. }
  288. sysret_t syscall_listen_pipeline(handle_t handle, dword_t timeout, pipe_connection_t *connection)
  289. {
  290. dword_t ret;
  291. process_t *proc;
  292. if (get_previous_mode() == USER_MODE)
  293. {
  294. proc = get_current_process();
  295. }
  296. else
  297. {
  298. proc = kernel_process;
  299. }
  300. pipeline_t *pipeline;
  301. if (!reference_by_handle(handle, OBJECT_PIPELINE, (object_t**)&pipeline)) return ERR_INVALID;
  302. lock_acquire_smart(&pipeline->lock);
  303. if (pipeline->status != PIPELINE_IDLE)
  304. {
  305. lock_release(&pipeline->lock);
  306. dereference(&pipeline->header);
  307. return ERR_BUSY;
  308. }
  309. pipeline->master_pid = proc->pid;
  310. pipeline->slave_pid = 0;
  311. pipeline->request_pipe = pipeline->response_pipe = NULL;
  312. pipeline->status = PIPELINE_ACCEPTING;
  313. lock_release(&pipeline->lock);
  314. wait_condition_t condition = { .type = WAIT_UNTIL_NOT_EQUAL, .pointer = &pipeline->status, .value = PIPELINE_ACCEPTING };
  315. wait_result_t result = scheduler_wait(&condition, timeout);
  316. lock_acquire_smart(&pipeline->lock);
  317. if (pipeline->status != PIPELINE_CONNECTING)
  318. {
  319. switch (result)
  320. {
  321. case WAIT_CONDITION_HIT:
  322. ret = pipeline->last_error;
  323. break;
  324. case WAIT_TIMED_OUT:
  325. ret = ERR_TIMEOUT;
  326. break;
  327. case WAIT_CANCELED:
  328. ret = ERR_CANCELED;
  329. }
  330. goto cleanup;
  331. }
  332. pipe_connection_t conn;
  333. conn.master_pid = pipeline->master_pid;
  334. conn.slave_pid = pipeline->slave_pid;
  335. conn.request_pipe = conn.response_pipe = INVALID_HANDLE;
  336. ret = connect_to_pipeline(pipeline, &conn);
  337. if (ret == ERR_SUCCESS)
  338. {
  339. EH_TRY
  340. {
  341. *connection = conn;
  342. }
  343. EH_CATCH
  344. {
  345. ret = ERR_BADPTR;
  346. }
  347. EH_DONE;
  348. }
  349. cleanup:
  350. pipeline->status = PIPELINE_IDLE;
  351. if (pipeline->request_pipe) dereference(&pipeline->request_pipe->header);
  352. if (pipeline->response_pipe) dereference(&pipeline->response_pipe->header);
  353. pipeline->request_pipe = pipeline->response_pipe = NULL;
  354. lock_release(&pipeline->lock);
  355. dereference(&pipeline->header);
  356. return ret;
  357. }
  358. sysret_t syscall_connect_pipeline(handle_t handle, access_flags_t access, pipe_connection_t *connection)
  359. {
  360. dword_t ret;
  361. process_t *proc;
  362. if (get_previous_mode() == USER_MODE)
  363. {
  364. proc = get_current_process();
  365. if (!check_usermode(connection, sizeof(pipe_connection_t))) return ERR_BADPTR;
  366. }
  367. else
  368. {
  369. proc = kernel_process;
  370. }
  371. pipeline_t *pipeline = NULL;
  372. if (!reference_by_handle(handle, OBJECT_PIPELINE, (object_t**)&pipeline))
  373. {
  374. ret = ERR_NOTFOUND;
  375. goto cleanup;
  376. }
  377. lock_acquire_smart(&pipeline->lock);
  378. if (pipeline->status != PIPELINE_ACCEPTING)
  379. {
  380. ret = ERR_BUSY;
  381. goto cleanup;
  382. }
  383. pipeline->slave_pid = proc->pid;
  384. ret = pipeline_create_pipes(pipeline);
  385. if (ret != ERR_SUCCESS)
  386. {
  387. pipeline->last_error = ret;
  388. goto cleanup;
  389. }
  390. pipe_connection_t conn;
  391. conn.master_pid = pipeline->master_pid;
  392. conn.slave_pid = pipeline->slave_pid;
  393. conn.request_pipe = INVALID_HANDLE;
  394. conn.response_pipe = INVALID_HANDLE;
  395. ret = connect_to_pipeline(pipeline, &conn);
  396. if (ret == ERR_SUCCESS)
  397. {
  398. pipeline->status = PIPELINE_CONNECTING;
  399. EH_TRY
  400. {
  401. *connection = conn;
  402. }
  403. EH_CATCH
  404. {
  405. ret = ERR_BADPTR;
  406. }
  407. EH_DONE;
  408. }
  409. else
  410. {
  411. pipeline->last_error = ret;
  412. pipeline->status = PIPELINE_FAILED;
  413. }
  414. cleanup:
  415. lock_release(&pipeline->lock);
  416. if (pipeline) dereference(&pipeline->header);
  417. return ret;
  418. }
  419. sysret_t syscall_read_pipe(handle_t handle, void *buffer, size_t *size, dword_t timeout)
  420. {
  421. pipe_t *pipe;
  422. void *safe_buffer = NULL;
  423. size_t safe_size = 0;
  424. if (get_previous_mode() == USER_MODE)
  425. {
  426. if (!check_usermode(size, sizeof(size_t))) return ERR_BADPTR;
  427. EH_TRY safe_size = *size;
  428. EH_CATCH EH_ESCAPE(return ERR_BADPTR);
  429. EH_DONE;
  430. if (!check_usermode(buffer, safe_size)) return ERR_BADPTR;
  431. safe_buffer = malloc(safe_size);
  432. if (safe_buffer == NULL) return ERR_NOMEMORY;
  433. }
  434. else
  435. {
  436. safe_buffer = buffer;
  437. safe_size = *size;
  438. }
  439. if (!reference_by_handle(handle, OBJECT_PIPE, (object_t**)&pipe)) return ERR_INVALID;
  440. dword_t ret = read_pipe(pipe, (byte_t*)safe_buffer, &safe_size, timeout);
  441. dereference(&pipe->header);
  442. if (get_previous_mode() == USER_MODE)
  443. {
  444. EH_TRY
  445. {
  446. memcpy(buffer, safe_buffer, safe_size);
  447. *size = safe_size;
  448. }
  449. EH_CATCH
  450. {
  451. ret = ERR_BADPTR;
  452. }
  453. EH_DONE;
  454. free(safe_buffer);
  455. }
  456. else
  457. {
  458. *size = safe_size;
  459. }
  460. return ret;
  461. }
  462. sysret_t syscall_write_pipe(handle_t handle, void *buffer, size_t size)
  463. {
  464. pipe_t *pipe;
  465. byte_t *safe_buffer = NULL;
  466. if (get_previous_mode() == USER_MODE)
  467. {
  468. if (!check_usermode(buffer, size)) return ERR_BADPTR;
  469. safe_buffer = (byte_t*)malloc(size);
  470. if (safe_buffer == NULL) return ERR_NOMEMORY;
  471. EH_TRY
  472. {
  473. memcpy(safe_buffer, buffer, size);
  474. }
  475. EH_CATCH
  476. {
  477. free(safe_buffer);
  478. EH_ESCAPE(return ERR_BADPTR);
  479. }
  480. EH_DONE;
  481. }
  482. else safe_buffer = (byte_t*)buffer;
  483. if (!reference_by_handle(handle, OBJECT_PIPE, (object_t**)&pipe)) return ERR_INVALID;
  484. dword_t ret = write_pipe(pipe, safe_buffer, size);
  485. dereference(&pipe->header);
  486. if (get_previous_mode() == USER_MODE)
  487. {
  488. EH_TRY memcpy(buffer, safe_buffer, size);
  489. EH_CATCH ret = ERR_BADPTR;
  490. EH_DONE;
  491. free(safe_buffer);
  492. }
  493. return ret;
  494. }