123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594 |
- /*
- * pipe.c
- *
- * Copyright (C) 2015 Aleksandar Andrejevic <theflash@sdf.lonestar.org>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- #include <pipe.h>
- #include <exception.h>
- #include <timer.h>
- #include <process.h>
- #include <thread.h>
- #include <syscalls.h>
- #include <heap.h>
- static dword_t create_pipe_internal(pipe_t **_pipe, dword_t flags)
- {
- pipe_t *pipe = (pipe_t*)malloc(sizeof(pipe_t));
- if (pipe == NULL) return ERR_NOMEMORY;
- init_object(&pipe->header, NULL, OBJECT_PIPE);
- init_pipe(pipe, flags);
- dword_t ret = create_object(&pipe->header);
- if (ret != ERR_SUCCESS)
- {
- free(pipe);
- return ret;
- }
- *_pipe = pipe;
- return ERR_SUCCESS;
- }
- dword_t read_pipe(pipe_t *pipe, void *buffer, size_t *size, dword_t timeout)
- {
- dword_t ret = ERR_SUCCESS;
- size_t requested_size = *size;
- lock_acquire(&pipe->lock);
- if (pipe->flags & PIPE_MESSAGE)
- {
- if (pipe->fifo.next == &pipe->fifo)
- {
- lock_release(&pipe->lock);
- wait_condition_t condition = { .type = WAIT_UNTIL_NOT_EQUAL, .pointer = (dword_t*)&pipe->fifo.next, .value = 0 };
- wait_result_t status = scheduler_wait(&condition, timeout);
- if (status != ERR_SUCCESS) return (status == WAIT_TIMED_OUT) ? ERR_TIMEOUT : ERR_CANCELED;
- lock_acquire(&pipe->lock);
- }
- pipe_message_entry_t *entry = CONTAINER_OF(pipe->fifo.next, pipe_message_entry_t, link);
- *size = entry->size;
- if (requested_size < entry->size)
- {
- ret = ERR_SMALLBUF;
- goto cleanup;
- }
- memcpy(buffer, entry->data, entry->size);
- list_remove(&entry->link);
- heap_free(&evictable_heap, entry);
- }
- else
- {
- byte_t *data = (byte_t*)buffer;
- *size = 0;
- while (*size < requested_size)
- {
- if (pipe->fifo.next == &pipe->fifo)
- {
- lock_release(&pipe->lock);
- wait_condition_t condition = { .type = WAIT_UNTIL_NOT_EQUAL, .pointer = (dword_t*)&pipe->fifo.next, .value = 0 };
- wait_result_t status = scheduler_wait(&condition, timeout);
- if (status != ERR_SUCCESS) return (status == WAIT_TIMED_OUT) ? ERR_TIMEOUT : ERR_CANCELED;
- lock_acquire(&pipe->lock);
- }
- pipe_fifo_entry_t *entry = CONTAINER_OF(pipe->fifo.next, pipe_fifo_entry_t, link);
- while ((*size < requested_size) && ((entry->start != entry->end) || entry->full))
- {
- data[(*size)++] = entry->data[entry->start++];
- entry->start %= PIPE_BLOCK_SIZE;
- entry->full = FALSE;
- }
- if ((entry->start == entry->end) && !entry->full)
- {
- list_remove(&entry->link);
- heap_free(&evictable_heap, entry);
- }
- }
- }
- pipe->bytes_ready -= *size;
- cleanup:
- lock_release(&pipe->lock);
- return ret;
- }
- dword_t write_pipe(pipe_t *pipe, const void *buffer, size_t size)
- {
- dword_t ret = ERR_SUCCESS;
- dword_t count = 0;
- lock_acquire(&pipe->lock);
- if (pipe->flags & PIPE_MESSAGE)
- {
- pipe_message_entry_t *entry = heap_alloc(&evictable_heap, sizeof(pipe_message_entry_t) + size);
- if (!entry)
- {
- ret = ERR_NOMEMORY;
- goto cleanup;
- }
- entry->size = size;
- memcpy(entry->data, buffer, size);
- list_append(&pipe->fifo, &entry->link);
- count = size;
- }
- else
- {
- const byte_t *data = (const byte_t*)buffer;
- pipe_fifo_entry_t *entry = (pipe->fifo.prev != &pipe->fifo) ? (pipe_fifo_entry_t*)pipe->fifo.prev : NULL;
- while (count < size)
- {
- if (!entry || entry->full)
- {
- entry = (pipe_fifo_entry_t*)heap_alloc(&evictable_heap, sizeof(pipe_fifo_entry_t));
- if (!entry)
- {
- ret = ERR_NOMEMORY;
- break;
- }
- entry->start = entry->end = 0;
- entry->full = FALSE;
- list_append(&pipe->fifo, &entry->link);
- }
- while ((count < size) && (entry->start != entry->end))
- {
- entry->data[entry->end++] = data[count++];
- entry->end %= PIPE_BLOCK_SIZE;
- }
- if (entry->start == entry->end) entry->full = TRUE;
- }
- }
- pipe->bytes_ready += count;
- cleanup:
- lock_release(&pipe->lock);
- return ret;
- }
- void pipe_cleanup(object_t *obj)
- {
- pipe_t *pipe = (pipe_t*)obj;
- while (pipe->fifo.next != &pipe->fifo)
- {
- pipe_fifo_entry_t *entry = CONTAINER_OF(pipe->fifo.next, pipe_fifo_entry_t, link);
- list_remove(&entry->link);
- free(entry);
- }
- }
- dword_t pipe_pre_wait(object_t *obj, void *parameter, wait_condition_t *condition)
- {
- pipe_t *pipe = (pipe_t*)obj;
- dword_t min_bytes = parameter ? (dword_t)parameter : 1;
- condition->type = WAIT_UNTIL_NOT_LESS;
- condition->pointer = &pipe->bytes_ready;
- condition->value = min_bytes;
- return ERR_SUCCESS;
- }
- sysret_t syscall_create_pipeline(const char *name, dword_t flags, access_flags_t access, handle_t *handle)
- {
- dword_t ret;
- handle_t safe_handle;
- const char *safe_name = NULL;
- if (get_previous_mode() == USER_MODE)
- {
- dword_t name_length = 0;
- EH_TRY name_length = strlen(name);
- EH_CATCH EH_ESCAPE(return ERR_BADPTR);
- EH_DONE;
- if (!check_usermode(name, name_length + 1)) return ERR_BADPTR;
- if (!check_usermode(handle, sizeof(handle_t))) return ERR_BADPTR;
- safe_name = copy_user_string(name);
- if (safe_name == NULL) return ERR_BADPTR;
- }
- else
- {
- safe_name = (const char*)name;
- }
- pipeline_t *pipeline = (pipeline_t*)malloc(sizeof(pipeline_t));
- if (pipeline == NULL)
- {
- ret = ERR_NOMEMORY;
- goto cleanup;
- }
- init_object(&pipeline->header, safe_name, OBJECT_PIPELINE);
- pipeline->flags = flags;
- lock_init(&pipeline->lock);
- pipeline->status = PIPELINE_IDLE;
- ret = create_object(&pipeline->header);
- if (ret != ERR_SUCCESS)
- {
- free(pipeline->header.name);
- free(pipeline);
- goto cleanup;
- }
- ret = open_object(&pipeline->header, access, &safe_handle);
- if (ret != ERR_SUCCESS) goto cleanup;
- EH_TRY
- {
- *handle = safe_handle;
- }
- EH_CATCH
- {
- syscall_close_object(safe_handle);
- ret = ERR_BADPTR;
- }
- EH_DONE;
- cleanup:
- dereference(&pipeline->header);
- if (get_previous_mode() == USER_MODE) free((void*)safe_name);
- return ret;
- }
- sysret_t syscall_open_pipeline(const char *name, access_flags_t access, handle_t *handle)
- {
- dword_t ret;
- handle_t safe_handle;
- const char *safe_name = NULL;
- if (get_previous_mode() == USER_MODE)
- {
- dword_t name_length = 0;
- EH_TRY name_length = strlen(name);
- EH_CATCH EH_ESCAPE(return ERR_BADPTR);
- EH_DONE;
- if (!check_usermode(name, name_length + 1)) return ERR_BADPTR;
- if (!check_usermode(handle, sizeof(handle_t))) return ERR_BADPTR;
- safe_name = copy_user_string(name);
- if (safe_name == NULL) return ERR_BADPTR;
- }
- else
- {
- safe_name = (const char*)name;
- }
- ret = open_object_by_name(safe_name, OBJECT_PIPELINE, access, &safe_handle);
- if (ret != ERR_SUCCESS) goto cleanup;
- EH_TRY
- {
- *handle = safe_handle;
- }
- EH_CATCH
- {
- ret = ERR_BADPTR;
- }
- EH_DONE;
- cleanup:
- if (get_previous_mode() == USER_MODE) free((void*)safe_name);
- return ret;
- }
- static dword_t pipeline_create_pipes(pipeline_t *pipeline)
- {
- dword_t request_flags = (pipeline->flags & PIPELINE_REQUEST_MESSAGE) ? PIPE_MESSAGE : 0;
- dword_t response_flags = (pipeline->flags & PIPELINE_RESPONSE_MESSAGE) ? PIPE_MESSAGE : 0;
- dword_t ret = create_pipe_internal(&pipeline->request_pipe, request_flags);
- if (ret != ERR_SUCCESS) return ret;
- if (!(pipeline->flags & PIPELINE_REQUEST_ONLY))
- {
- ret = create_pipe_internal(&pipeline->response_pipe, response_flags);
- if (ret != ERR_SUCCESS)
- {
- dereference(&pipeline->request_pipe->header);
- pipeline->request_pipe = NULL;
- }
- }
- return ret;
- }
- static dword_t connect_to_pipeline(pipeline_t *pipeline, pipe_connection_t *conn)
- {
- lock_acquire_smart(&pipeline->lock);
- dword_t ret = open_object(&pipeline->request_pipe->header, 0, &conn->request_pipe);
- if (ret != ERR_SUCCESS) goto cleanup;
- if (pipeline->response_pipe)
- {
- ret = open_object(&pipeline->response_pipe->header, 0, &conn->response_pipe);
- if (ret != ERR_SUCCESS) syscall_close_object(conn->response_pipe);
- }
- cleanup:
- lock_release(&pipeline->lock);
- return ret;
- }
- sysret_t syscall_listen_pipeline(handle_t handle, dword_t timeout, pipe_connection_t *connection)
- {
- dword_t ret;
- process_t *proc;
- if (get_previous_mode() == USER_MODE)
- {
- proc = get_current_process();
- }
- else
- {
- proc = kernel_process;
- }
- pipeline_t *pipeline;
- if (!reference_by_handle(handle, OBJECT_PIPELINE, (object_t**)&pipeline)) return ERR_INVALID;
- lock_acquire_smart(&pipeline->lock);
- if (pipeline->status != PIPELINE_IDLE)
- {
- lock_release(&pipeline->lock);
- dereference(&pipeline->header);
- return ERR_BUSY;
- }
- pipeline->master_pid = proc->pid;
- pipeline->slave_pid = 0;
- pipeline->request_pipe = pipeline->response_pipe = NULL;
- pipeline->status = PIPELINE_ACCEPTING;
- lock_release(&pipeline->lock);
- wait_condition_t condition = { .type = WAIT_UNTIL_NOT_EQUAL, .pointer = &pipeline->status, .value = PIPELINE_ACCEPTING };
- wait_result_t result = scheduler_wait(&condition, timeout);
- lock_acquire_smart(&pipeline->lock);
- if (pipeline->status != PIPELINE_CONNECTING)
- {
- switch (result)
- {
- case WAIT_CONDITION_HIT:
- ret = pipeline->last_error;
- break;
- case WAIT_TIMED_OUT:
- ret = ERR_TIMEOUT;
- break;
- case WAIT_CANCELED:
- ret = ERR_CANCELED;
- }
- goto cleanup;
- }
- pipe_connection_t conn;
- conn.master_pid = pipeline->master_pid;
- conn.slave_pid = pipeline->slave_pid;
- conn.request_pipe = conn.response_pipe = INVALID_HANDLE;
- ret = connect_to_pipeline(pipeline, &conn);
- if (ret == ERR_SUCCESS)
- {
- EH_TRY
- {
- *connection = conn;
- }
- EH_CATCH
- {
- ret = ERR_BADPTR;
- }
- EH_DONE;
- }
- cleanup:
- pipeline->status = PIPELINE_IDLE;
- if (pipeline->request_pipe) dereference(&pipeline->request_pipe->header);
- if (pipeline->response_pipe) dereference(&pipeline->response_pipe->header);
- pipeline->request_pipe = pipeline->response_pipe = NULL;
- lock_release(&pipeline->lock);
- dereference(&pipeline->header);
- return ret;
- }
- sysret_t syscall_connect_pipeline(handle_t handle, access_flags_t access, pipe_connection_t *connection)
- {
- dword_t ret;
- process_t *proc;
- if (get_previous_mode() == USER_MODE)
- {
- proc = get_current_process();
- if (!check_usermode(connection, sizeof(pipe_connection_t))) return ERR_BADPTR;
- }
- else
- {
- proc = kernel_process;
- }
- pipeline_t *pipeline = NULL;
- if (!reference_by_handle(handle, OBJECT_PIPELINE, (object_t**)&pipeline))
- {
- ret = ERR_NOTFOUND;
- goto cleanup;
- }
- lock_acquire_smart(&pipeline->lock);
- if (pipeline->status != PIPELINE_ACCEPTING)
- {
- ret = ERR_BUSY;
- goto cleanup;
- }
- pipeline->slave_pid = proc->pid;
- ret = pipeline_create_pipes(pipeline);
- if (ret != ERR_SUCCESS)
- {
- pipeline->last_error = ret;
- goto cleanup;
- }
- pipe_connection_t conn;
- conn.master_pid = pipeline->master_pid;
- conn.slave_pid = pipeline->slave_pid;
- conn.request_pipe = INVALID_HANDLE;
- conn.response_pipe = INVALID_HANDLE;
- ret = connect_to_pipeline(pipeline, &conn);
- if (ret == ERR_SUCCESS)
- {
- pipeline->status = PIPELINE_CONNECTING;
- EH_TRY
- {
- *connection = conn;
- }
- EH_CATCH
- {
- ret = ERR_BADPTR;
- }
- EH_DONE;
- }
- else
- {
- pipeline->last_error = ret;
- pipeline->status = PIPELINE_FAILED;
- }
- cleanup:
- lock_release(&pipeline->lock);
- if (pipeline) dereference(&pipeline->header);
- return ret;
- }
- sysret_t syscall_read_pipe(handle_t handle, void *buffer, size_t *size, dword_t timeout)
- {
- pipe_t *pipe;
- void *safe_buffer = NULL;
- size_t safe_size = 0;
- if (get_previous_mode() == USER_MODE)
- {
- if (!check_usermode(size, sizeof(size_t))) return ERR_BADPTR;
- EH_TRY safe_size = *size;
- EH_CATCH EH_ESCAPE(return ERR_BADPTR);
- EH_DONE;
- if (!check_usermode(buffer, safe_size)) return ERR_BADPTR;
- safe_buffer = malloc(safe_size);
- if (safe_buffer == NULL) return ERR_NOMEMORY;
- }
- else
- {
- safe_buffer = buffer;
- safe_size = *size;
- }
- if (!reference_by_handle(handle, OBJECT_PIPE, (object_t**)&pipe)) return ERR_INVALID;
- dword_t ret = read_pipe(pipe, (byte_t*)safe_buffer, &safe_size, timeout);
- dereference(&pipe->header);
- if (get_previous_mode() == USER_MODE)
- {
- EH_TRY
- {
- memcpy(buffer, safe_buffer, safe_size);
- *size = safe_size;
- }
- EH_CATCH
- {
- ret = ERR_BADPTR;
- }
- EH_DONE;
- free(safe_buffer);
- }
- else
- {
- *size = safe_size;
- }
- return ret;
- }
- sysret_t syscall_write_pipe(handle_t handle, void *buffer, size_t size)
- {
- pipe_t *pipe;
- byte_t *safe_buffer = NULL;
- if (get_previous_mode() == USER_MODE)
- {
- if (!check_usermode(buffer, size)) return ERR_BADPTR;
- safe_buffer = (byte_t*)malloc(size);
- if (safe_buffer == NULL) return ERR_NOMEMORY;
- EH_TRY
- {
- memcpy(safe_buffer, buffer, size);
- }
- EH_CATCH
- {
- free(safe_buffer);
- EH_ESCAPE(return ERR_BADPTR);
- }
- EH_DONE;
- }
- else safe_buffer = (byte_t*)buffer;
- if (!reference_by_handle(handle, OBJECT_PIPE, (object_t**)&pipe)) return ERR_INVALID;
- dword_t ret = write_pipe(pipe, safe_buffer, size);
- dereference(&pipe->header);
- if (get_previous_mode() == USER_MODE)
- {
- EH_TRY memcpy(buffer, safe_buffer, size);
- EH_CATCH ret = ERR_BADPTR;
- EH_DONE;
- free(safe_buffer);
- }
- return ret;
- }
|