environment variables for processes

This commit is contained in:
Benji Dial 2025-12-29 18:27:34 -05:00
parent 285da1dc46
commit 7d90ac7d3d
11 changed files with 293 additions and 19 deletions

View file

@ -18,6 +18,7 @@
#pragma once #pragma once
#include <kernel-public/framebuffer.h> #include <kernel-public/framebuffer.h>
#include <kernel-public/process.h>
#include <kernel-public/files.h> #include <kernel-public/files.h>
#include <kernel-public/input.h> #include <kernel-public/input.h>
#include <kernel-public/ipc.h> #include <kernel-public/ipc.h>
@ -41,7 +42,7 @@ void *map_pages(uint64_t count);
void sleep_ms(uint64_t ms_to_sleep); void sleep_ms(uint64_t ms_to_sleep);
//1 on success, 0 on failure. //1 on success, 0 on failure.
int start_elf(const char *path); int start_elf(const char *path, const struct process_start_info *info);
enum ipc_dgram_result ipc_create_dgram_receiver(const char *address, ipc_dgram_receiver_handle_t *handle_out); enum ipc_dgram_result ipc_create_dgram_receiver(const char *address, ipc_dgram_receiver_handle_t *handle_out);
enum ipc_dgram_result ipc_create_dgram_sender(const char *address, ipc_dgram_sender_handle_t *handle_out); enum ipc_dgram_result ipc_create_dgram_sender(const char *address, ipc_dgram_sender_handle_t *handle_out);
@ -56,3 +57,11 @@ enum ipc_dgram_result ipc_send_dgram(ipc_dgram_sender_handle_t handle, const voi
//f should not return //f should not return
void create_thread(void (*f)(uint64_t x), uint64_t x); void create_thread(void (*f)(uint64_t x), uint64_t x);
//value_space must be positive at entry.
//looks up the environment variable with this key. if there is no such
//variable, sets value_space to 0. otherwise, sets value_space to the
//length of the value including null terminator (which will be positive),
//and if that is at most what value_space was set to at entry, also
//copies the value including null terminator to value_out.
void get_envvar(const char *key, char *value_out, int *value_space);

View file

@ -0,0 +1,32 @@
/* Calcite, include/kernel-public/process.h
* Copyright 2025 Benji Dial
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
struct process_start_info {
//keys of envvars to copy to new process.
const char **forwared_envvars;
int forwared_envvar_count;
//keys and values to set in new process.
//entry i has key set_envvars[i * 2] and value set_envvars[i * 2 + 1].
//set_envvar_count is number of envvars, not twice that.
const char **set_envvars;
int set_envvar_count;
};

View file

@ -32,5 +32,6 @@ enum {
SYSCALL_IPC_CREATE_DGRAM_SENDER, SYSCALL_IPC_CREATE_DGRAM_SENDER,
SYSCALL_IPC_RECEIVE_DGRAM, SYSCALL_IPC_RECEIVE_DGRAM,
SYSCALL_IPC_SEND_DGRAM, SYSCALL_IPC_SEND_DGRAM,
SYSCALL_CREATE_THREAD SYSCALL_CREATE_THREAD,
SYSCALL_GET_ENVVAR
}; };

View file

@ -271,6 +271,7 @@ static const char *cmdline_look_up(const char *key) {
register_syscall(SYSCALL_IPC_RECEIVE_DGRAM, (void *)&syscall_ipc_receive_dgram); register_syscall(SYSCALL_IPC_RECEIVE_DGRAM, (void *)&syscall_ipc_receive_dgram);
register_syscall(SYSCALL_IPC_SEND_DGRAM, (void *)&syscall_ipc_send_dgram); register_syscall(SYSCALL_IPC_SEND_DGRAM, (void *)&syscall_ipc_send_dgram);
register_syscall(SYSCALL_CREATE_THREAD, (void *)&syscall_create_thread); register_syscall(SYSCALL_CREATE_THREAD, (void *)&syscall_create_thread);
register_syscall(SYSCALL_GET_ENVVAR, (void *)&syscall_get_envvar);
//probe for drives //probe for drives
@ -298,7 +299,7 @@ static const char *cmdline_look_up(const char *key) {
init_scheduler(); init_scheduler();
if (!start_elf("root://calcite/apps/init/init.elf")) if (start_elf("root://calcite/apps/init/init.elf") == 0)
panic("could not start init.elf") panic("could not start init.elf")
resume_next_continuation(); resume_next_continuation();

View file

@ -26,6 +26,7 @@
#include "heap.h" #include "heap.h"
#include "fs.h" #include "fs.h"
#include <kernel-public/process.h>
#include <kernel-public/files.h> #include <kernel-public/files.h>
#include <kernel-public/ipc.h> #include <kernel-public/ipc.h>
@ -66,6 +67,7 @@ void create_process(struct process *process_out) {
process_out->n_threads = 0; process_out->n_threads = 0;
process_out->files = 0; process_out->files = 0;
process_out->ipc_dgram_handles = 0; process_out->ipc_dgram_handles = 0;
process_out->env_pairs = 0;
} }
@ -345,7 +347,8 @@ int load_elf(
// all other registers zeroed // all other registers zeroed
extern uint8_t thread_start; extern uint8_t thread_start;
int start_elf(const char *uri) { //returns 0 on failure
struct process *start_elf(const char *uri) {
struct process *process = heap_alloc(sizeof(struct process)); struct process *process = heap_alloc(sizeof(struct process));
create_process(process); create_process(process);
@ -376,7 +379,7 @@ int start_elf(const char *uri) {
add_to_queue(&ready_continuations, &ci); add_to_queue(&ready_continuations, &ci);
return 1; return process;
} }
@ -399,14 +402,45 @@ void syscall_create_thread(void (*f)(uint64_t), uint64_t x) {
} }
int syscall_start_elf(const char *uri) { int syscall_start_elf(const char *path, const struct process_start_info *info) {
assert(running_thread != 0) assert(running_thread != 0)
if (!is_mapped_readable_string(running_thread->process, uri)) if (!is_mapped_readable_string(running_thread->process, path) ||
!is_mapped_readable(
running_thread->process, info, sizeof(struct process_start_info)) ||
!is_mapped_readable(
running_thread->process,
info->forwared_envvars,
info->forwared_envvar_count * sizeof(const char *)) ||
!is_mapped_readable(
running_thread->process,
info->set_envvars,
2 * info->set_envvar_count * sizeof(const char *)))
syscall_illegal_args(); syscall_illegal_args();
return start_elf(uri); for (int i = 0; i < info->forwared_envvar_count; ++i)
if (!is_mapped_readable_string(running_thread->process, info->forwared_envvars[i]))
syscall_illegal_args();
for (int i = 0; i < 2 * info->set_envvar_count; ++i)
if (!is_mapped_readable_string(running_thread->process, info->set_envvars[i]))
syscall_illegal_args();
struct process *process = start_elf(path);
if (process == 0)
return 0;
for (int i = 0; i < info->forwared_envvar_count; ++i) {
struct process_env_pair *pair = get_envvar(running_thread->process, info->forwared_envvars[i]);
if (pair != 0)
set_envvar(process, info->forwared_envvars[i], pair->value);
}
for (int i = 0; i < info->set_envvar_count; ++i)
set_envvar(process, info->set_envvars[2 * i], info->set_envvars[2 * i + 1]);
return 1;
} }
@ -428,6 +462,16 @@ void destroy_process(struct process *process) {
process->ipc_dgram_handles_buffer_size * sizeof(struct process_ipc_dgram_handle_info)); process->ipc_dgram_handles_buffer_size * sizeof(struct process_ipc_dgram_handle_info));
} }
if (process->env_pairs) {
for (int i = 0; i < process->env_pairs_buffer_size; ++i)
if (process->env_pairs[i].key_length != 0) {
heap_dealloc(process->env_pairs[i].key, process->env_pairs[i].key_length + 1);
heap_dealloc(process->env_pairs[i].value, process->env_pairs[i].value_length + 1);
}
heap_dealloc(
process->env_pairs, process->env_pairs_buffer_size * sizeof(struct process_env_pair));
}
for (int p3i = 0; p3i < 512; ++p3i) for (int p3i = 0; p3i < 512; ++p3i)
if (process->p3_virtual_base[p3i]) { if (process->p3_virtual_base[p3i]) {
for (int p2i = 0; p2i < 512; ++p2i) for (int p2i = 0; p2i < 512; ++p2i)
@ -804,3 +848,113 @@ enum ipc_dgram_result syscall_ipc_send_dgram(
return send_ipc_dgram(box, data, bytes); return send_ipc_dgram(box, data, bytes);
} }
#define INITIAL_ENVVAR_BUFFER_SIZE 32
void set_envvar(struct process *process, const char *key, const char *value) {
if (!process->env_pairs) {
process->env_pairs = heap_alloc(INITIAL_ENVVAR_BUFFER_SIZE * sizeof(struct process_env_pair));
process->env_pairs_buffer_size = INITIAL_ENVVAR_BUFFER_SIZE;
for (int i = 0; i < INITIAL_ENVVAR_BUFFER_SIZE; ++i)
process->env_pairs[i].key_length = 0;
}
int key_length = 0;
while (key[key_length] != 0)
++key_length;
int value_length = 0;
while (value[value_length] != 0)
++value_length;
assert(key_length != 0)
assert(value_length != 0)
for (int i = 0; i < process->env_pairs_buffer_size; ++i) {
if (process->env_pairs[i].key_length != key_length)
continue;
for (int j = 0; j < key_length; ++j)
if (process->env_pairs[i].key[j] != key[j])
continue;
heap_dealloc(process->env_pairs[i].value, process->env_pairs[i].value_length + 1);
char *value_copy = heap_alloc(value_length + 1);
memcpy(value_copy, value, value_length + 1);
process->env_pairs[i].value = value_copy;
process->env_pairs[i].value_length = value_length;
return;
}
struct process_env_pair *pair;
for (int i = 0; i < process->env_pairs_buffer_size; ++i)
if (process->env_pairs[i].key_length == 0) {
pair = &process->env_pairs[i];
goto got_pair;
}
double_buffer_zero(
(void **)&process->env_pairs,
&process->env_pairs_buffer_size,
sizeof(struct process_env_pair));
pair = &process->env_pairs[process->env_pairs_buffer_size / 2];
got_pair:
char *key_copy = heap_alloc(key_length + 1);
memcpy(key_copy, key, key_length + 1);
pair->key = key_copy;
pair->key_length = key_length;
char *value_copy = heap_alloc(value_length + 1);
memcpy(value_copy, value, value_length + 1);
pair->value = value_copy;
pair->value_length = value_length;
}
struct process_env_pair *get_envvar(struct process *process, const char *key) {
int key_length = 0;
while (key[key_length] != 0)
++key_length;
assert(key_length > 0)
for (int i = 0; i < process->env_pairs_buffer_size; ++i) {
if (process->env_pairs[i].key_length != key_length)
continue;
for (int j = 0; j < key_length; ++j)
if (process->env_pairs[i].key[j] != key[j])
continue;
return &process->env_pairs[i];
}
return 0;
}
void syscall_get_envvar(const char *key, char *value_out, int *value_space) {
assert(running_thread != 0)
if (!is_mapped_readable_string(running_thread->process, key) ||
!is_mapped_writable(running_thread->process, value_space, sizeof(int)) ||
*value_space <= 0 ||
!is_mapped_writable(running_thread->process, value_out, *value_space))
syscall_illegal_args();
struct process_env_pair *pair = get_envvar(running_thread->process, key);
if (pair == 0) {
*value_space = 0;
return;
}
if (*value_space < pair->value_length + 1) {
*value_space = pair->value_length + 1;
return;
}
*value_space = pair->value_length + 1;
memcpy(value_out, pair->value, pair->value_length + 1);
}

View file

@ -20,6 +20,7 @@
#include "fs.h" #include "fs.h"
#include <kernel-public/framebuffer.h> #include <kernel-public/framebuffer.h>
#include <kernel-public/process.h>
#include <kernel-public/files.h> #include <kernel-public/files.h>
#include <kernel-public/ipc.h> #include <kernel-public/ipc.h>
@ -38,6 +39,16 @@ struct process_ipc_dgram_handle_info {
int is_receiver; int is_receiver;
}; };
//lengths don't include null terminators, buffers do.
//key and value should not be empty.
struct process_env_pair {
//0 for unused
int key_length;
int value_length;
char *key;
char *value;
};
struct process { struct process {
uint64_t p4_physical_base; uint64_t p4_physical_base;
@ -55,6 +66,9 @@ struct process {
struct process_ipc_dgram_handle_info *ipc_dgram_handles; struct process_ipc_dgram_handle_info *ipc_dgram_handles;
int ipc_dgram_handles_buffer_size; int ipc_dgram_handles_buffer_size;
struct process_env_pair *env_pairs;
int env_pairs_buffer_size;
//0 for missing levels. just bottom p3 of address space. //0 for missing levels. just bottom p3 of address space.
uint64_t *p2_virtual_bases[512]; uint64_t *p2_virtual_bases[512];
uint64_t **p1_virtual_bases[512]; uint64_t **p1_virtual_bases[512];
@ -104,13 +118,13 @@ int load_elf(
struct process *process, uint64_t *entry_out, struct process *process, uint64_t *entry_out,
const struct fs_info *fs_info, void *fs_node); const struct fs_info *fs_info, void *fs_node);
//returns 0 on failure, 1 on success. //returns 0 on failure.
//creates a process and a thread in that process, loads the elf into the process, //creates a process and a thread in that process, loads the elf into the process,
//and schedules a ready task that sets the running thread to the new thread and //and schedules a ready task that sets the running thread to the new thread and
//starts user mode at the elf's entry point. //starts user mode at the elf's entry point.
int start_elf(const char *uri); struct process *start_elf(const char *uri);
int syscall_start_elf(const char *uri); int syscall_start_elf(const char *path, const struct process_start_info *info);
void destroy_process(struct process *process); void destroy_process(struct process *process);
void destroy_thread(struct thread *thread); void destroy_thread(struct thread *thread);
@ -155,3 +169,11 @@ enum ipc_dgram_result syscall_ipc_send_dgram(
//f should not return. //f should not return.
void syscall_create_thread(void (*f)(uint64_t x), uint64_t x); void syscall_create_thread(void (*f)(uint64_t x), uint64_t x);
void set_envvar(struct process *process, const char *key, const char *value);
//0 if unset
struct process_env_pair *get_envvar(struct process *process, const char *key);
//see comment in include/calcite/syscalls.h
void syscall_get_envvar(const char *key, char *value_out, int *value_space);

View file

@ -16,6 +16,7 @@
*/ */
#include "utility.h" #include "utility.h"
#include "heap.h"
int strequ(const char *str1, const char *str2) { int strequ(const char *str1, const char *str2) {
while (1) { while (1) {
@ -35,3 +36,12 @@ uint32_t end_swap_u32(uint32_t value) {
(((value >> 8) & 0xff) << 16) | (((value >> 8) & 0xff) << 16) |
((value & 0xff) << 24); ((value & 0xff) << 24);
} }
void double_buffer_zero(void **buffer, int *length, uint64_t bytes_per_entry) {
void *new_buffer = heap_alloc(2 * *length * bytes_per_entry);
memcpy(new_buffer, *buffer, *length * bytes_per_entry);
heap_dealloc(*buffer, *length * bytes_per_entry);
memzero(new_buffer + *length * bytes_per_entry, *length * bytes_per_entry);
*buffer = new_buffer;
*length *= 2;
}

View file

@ -27,3 +27,10 @@ void memzero(void *start, uint64_t bytes);
//swaps the endianness of the value //swaps the endianness of the value
uint32_t end_swap_u32(uint32_t value); uint32_t end_swap_u32(uint32_t value);
//1. allocates a new buffer with 2 * length * bytes_per_entry bytes
//2. copies length * bytes_per_entry bytes from old buffer to new buffer
//3. deallocates old buffer
//4. zeroes rest of new buffer
//5. sets buffer and length to new buffer and twice length
void double_buffer_zero(void **buffer, int *length, uint64_t bytes_per_entry);

View file

@ -15,7 +15,7 @@
* with this program. If not, see <https://www.gnu.org/licenses/>. * with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include "kernel-public/ipc.h" #include <kernel-public/ipc.h>
#include <calcite/syscalls.h> #include <calcite/syscalls.h>
#include <silver/pam.h> #include <silver/pam.h>
@ -72,20 +72,43 @@ static void copy_frame() {
end_thread(); end_thread();
} }
static int strequ(const char *str1, const char *str2) {
while (1) {
if (*str1 == 0 && *str2 == 0)
return 1;
if (*str1 != *str2)
return 0;
++str1;
++str2;
}
}
static int memequ(const char *str1, const char *str2, int length) {
for (int i = 0; i < length; ++i)
if (str1[i] != str2[i])
return 0;
return 1;
}
void main() { void main() {
map_framebuffer(&fb_info); map_framebuffer(&fb_info);
create_image(fb_info.fb_width, fb_info.fb_height, &fbb); create_image(fb_info.fb_width, fb_info.fb_height, &fbb);
char buffer[100];
int bytes = 100;
get_envvar("hello", buffer, &bytes);
if (bytes != 6 || !strequ(buffer, "world"))
panic();
ipc_dgram_receiver_handle_t receiver; ipc_dgram_receiver_handle_t receiver;
if (ipc_create_dgram_receiver("calcite-test", &receiver) != IPR_SUCCESS) if (ipc_create_dgram_receiver("calcite-test", &receiver) != IPR_SUCCESS)
panic(); panic();
char buffer[5]; bytes = 100;
int bytes = 5;
if (ipc_receive_dgram(receiver, buffer, &bytes) != IPR_SUCCESS || if (ipc_receive_dgram(receiver, buffer, &bytes) != IPR_SUCCESS ||
bytes != 5 || bytes != 5 || !memequ(buffer, "hello", 5))
buffer[0] != 'h' || buffer[1] != 'e' || buffer[2] != 'l' || buffer[3] != 'l' || buffer[4] != 'o')
panic(); panic();
if (!load_pam("root://calcite/apps/hello/pointer.pam", &cursor_image)) if (!load_pam("root://calcite/apps/hello/pointer.pam", &cursor_image))

View file

@ -15,6 +15,7 @@
* with this program. If not, see <https://www.gnu.org/licenses/>. * with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include <kernel-public/process.h>
#include <calcite/file-streams.h> #include <calcite/file-streams.h>
#include <kernel-public/files.h> #include <kernel-public/files.h>
#include <kernel-public/ipc.h> #include <kernel-public/ipc.h>
@ -76,7 +77,15 @@ void main() {
continue; continue;
line[line_length] = 0; line[line_length] = 0;
start_elf(line);
const char *set_envvars[2] = { "hello", "world" };
struct process_start_info psi;
psi.forwared_envvar_count = 0;
psi.set_envvar_count = 1;
psi.set_envvars = set_envvars;
start_elf(line, &psi);
} }

View file

@ -16,6 +16,7 @@
*/ */
#include <kernel-public/syscall-numbers.h> #include <kernel-public/syscall-numbers.h>
#include <kernel-public/process.h>
#include <kernel-public/files.h> #include <kernel-public/files.h>
#include <kernel-public/ipc.h> #include <kernel-public/ipc.h>
#include <calcite/syscalls.h> #include <calcite/syscalls.h>
@ -66,8 +67,8 @@ void sleep_ms(uint64_t ms_to_sleep) {
do_syscall(ms_to_sleep, 0, 0, SYSCALL_SLEEP_MS); do_syscall(ms_to_sleep, 0, 0, SYSCALL_SLEEP_MS);
} }
int start_elf(const char *uri) { int start_elf(const char *path, const struct process_start_info *info) {
return do_syscall((uint64_t)uri, 0, 0, SYSCALL_START_ELF); return do_syscall((uint64_t)path, (uint64_t)info, 0, SYSCALL_START_ELF);
} }
enum ipc_dgram_result ipc_create_dgram_receiver(const char *address, ipc_dgram_receiver_handle_t *handle_out) { enum ipc_dgram_result ipc_create_dgram_receiver(const char *address, ipc_dgram_receiver_handle_t *handle_out) {
@ -92,3 +93,8 @@ enum ipc_dgram_result ipc_send_dgram(ipc_dgram_sender_handle_t handle, const voi
void create_thread(void (*f)(uint64_t x), uint64_t x) { void create_thread(void (*f)(uint64_t x), uint64_t x) {
do_syscall((uint64_t)f, x, 0, SYSCALL_CREATE_THREAD); do_syscall((uint64_t)f, x, 0, SYSCALL_CREATE_THREAD);
} }
void get_envvar(const char *key, char *value_out, int *value_space) {
do_syscall(
(uint64_t)key, (uint64_t)value_out, (uint64_t)value_space, SYSCALL_GET_ENVVAR);
}