1/* 2 * Copyright 2016, Data61 3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO) 4 * ABN 41 687 119 230. 5 * 6 * This software may be distributed and modified according to the terms of 7 * the BSD 2-Clause license. Note that NO WARRANTY is provided. 8 * See "LICENSE_BSD2.txt" for details. 9 * 10 * @TAG(D61_BSD) 11 */ 12 13#include <stdio.h> 14#include <stdlib.h> 15#include <assert.h> 16#include <string.h> 17#include <time.h> 18#include <refos/refos.h> 19#include <refos-util/init.h> 20#include <refos-io/stdio.h> 21#include <refos-rpc/proc_client.h> 22#include <refos-rpc/proc_client_helper.h> 23#include <unistd.h> 24#include <autoconf.h> 25 26#include "print_time.h" 27 28/*! @file 29 @brief A simple terminal program for RefOS. 30 31 This is a rather minimalistic and hacky terminal app for RefOS. A better console such as bash, 32 or ash, may be ported in the future. The terminal app runs as the first userland program, and 33 presents an interactive shell. It's possible to launcher another instance of the terminal 34 program using the terminal program. 35*/ 36 37#define TERMINAL_INPUT_ARG_LENGTH 10 38#define TERMINAL_INPUT_ARG_COUNT 10 39#define TERMINAL_INPUT_BUFFER_SIZE 512 40#define TERMINAL_NEW_LINE_CHAR '\r' 41#define TERMINAL_DELETE_CHAR 127 42#define TERMINAL_CLEAR_SCREEN "\e[2J\e[1;1H" 43 44static char *args[TERMINAL_INPUT_ARG_COUNT]; 45static char exitProgram = 0; 46extern char **__environ; /*! < POSIX standard, from MUSLC lib. */ 47 48/*! @brief Print a heart-warming welcome banner. */ 49static void 50terminal_startup_message(void) 51{ 52 printf(" ______ ______ ______ ______ ______ \n" 53 "/\\ == \\ /\\ ___\\ /\\ ___\\ /\\ __ \\ /\\ ___\\ \n" 54 "\\ \\ __< \\ \\ __\\ \\ \\ __\\ \\ \\ \\/\\ \\ \\ \\___ \\ \n" 55 " \\ \\_\\ \\_\\ \\ \\_____\\ \\ \\_\\ \\ \\_____\\ \\/\\_____\\ \n" 56 " \\/_/ /_/ \\/_____/ \\/_/ \\/_____/ \\/_____/ \n\n" 57 "-----------------------------------------------------\n" 58 " Built on the seL4 microkernel.\n" 59 " (c) Copyright 2016 Data61, CSIRO\n" 60 "-----------------------------------------------------\n"); 61} 62 63/*! @brief Print some quick help. */ 64static void 65terminal_printhelp(void) 66{ 67 printf("RefOS Terminal Help:\n" 68 " clear - Clear the screen.\n" 69 " help - Display this help screen.\n" 70 " exec - Run an executable.\n" 71 #if CONFIG_APP_TETRIS 72 " exec fileserv/tetris - Run tetris game.\n" 73 #endif 74 #if CONFIG_APP_SNAKE 75 " exec fileserv/snake - Run snake game.\n" 76 #endif 77 #if CONFIG_APP_NETHACK 78 " exec fileserv/nethack - Run Nethack 3.4.3 game.\n" 79 #endif 80 #if CONFIG_APP_TEST_USER 81 " exec fileserv/test_user - Run RefOS userland tests.\n" 82 #endif 83 " exec fileserv/terminal - Run another instance of RefOS terminal.\n" 84 " cd /fileserv/ - Change current working directory.\n" 85 " printenv - Print all environment variables.\n" 86 " setenv - Set an environment variable.\n" 87 " time - Display the current system time.\n" 88 " exit - Exit RefOS terminal.\n"); 89} 90 91/*! @brief Execute a program. */ 92static void 93terminal_exec(void) 94{ 95 int status; 96 if (!args[1]) { 97 printf("exec: missing parameter.\n"); 98 return; 99 } 100 char tempBuffer[1024]; 101 snprintf(tempBuffer, 1024, "%s%s", getenv("PWD"), args[1]); 102 proc_new_proc(tempBuffer, "", true, 71, &status); 103 104 if (status == EFILENOTFOUND) { 105 printf("-terminal: %s: application not found\n", args[1]); 106 } 107} 108 109/*! @brief Evaluate a command. */ 110static void 111terminal_evaluate_command(char *inputBuffer) 112{ 113 if (!args[0]) return; 114 115 /* Crazy string parsing can go here. */ 116 if (!strcmp(args[0], "exec")) { 117 terminal_exec(); 118 } else if (!strcmp(args[0], "clear") || !strcmp(args[0], "cls") || !strcmp(args[0], "reset")) { 119 printf("%s", TERMINAL_CLEAR_SCREEN); 120 } else if (!strcmp(args[0], "exit") || !strcmp(args[0], "quit")) { 121 exitProgram = 1; 122 } else if (!strcmp(args[0], "help")) { 123 terminal_printhelp(); 124 } else if (!strcmp(args[0], "time")) { 125 time_t rawTime = time(NULL); 126 struct tm *gmtTime = gmtime(&rawTime); 127 struct tm *localTime = localtime(&rawTime); 128 printf("Raw epoch time is %llu\n", (uint64_t) rawTime); 129 printf("Current GMT time is %s", refos_print_time(gmtTime)); 130 printf("Current local time (%s) is %s", getenv("TZ"), refos_print_time(localTime)); 131 } else if (!strcmp(args[0], "printenv")) { 132 for (int i = 0; __environ[i]; i++) { 133 printf("%s\n", __environ[i]); 134 } 135 } else if (!strcmp(args[0], "setenv")) { 136 if (args[1] && args[2]) { 137 setenv(args[1], args[2], true); 138 } 139 else { 140 printf("setenv: usage: setenv <VARIABLE> <VALUE>\n"); 141 } 142 } else if (!strcmp(args[0], "cd") && args[1]) { 143 setenv("PWD", args[1], true); 144 } else { 145 printf("-terminal: %s: command not found\n", inputBuffer); 146 } 147} 148 149/*! @brief Input a new command. */ 150static void 151terminal_get_input(char *inputBuffer) 152{ 153 printf("refos:%s$ ", getenv("PWD")); 154 fflush(stdout); 155 156 int i = 0; inputBuffer[0] = '\0'; 157 for (i = 0; i < TERMINAL_INPUT_BUFFER_SIZE;) { 158 inputBuffer[i] = getchar(); 159 if (inputBuffer[i] == TERMINAL_NEW_LINE_CHAR) { 160 printf("\n"); 161 inputBuffer[i] = '\0'; 162 return; 163 } else if (inputBuffer[i] == TERMINAL_DELETE_CHAR) { 164 if (i > 0) { 165 printf("\b \b"); 166 fflush(stdout); 167 inputBuffer[--i] = '\0'; 168 } 169 } else { 170 printf("%c", inputBuffer[i]); 171 fflush(stdout); 172 i++; 173 } 174 } 175 inputBuffer[TERMINAL_INPUT_BUFFER_SIZE] = '\0'; 176 printf("\n"); 177} 178 179/*! @brief Reset the input buffer. */ 180static inline void 181terminal_reset_input(void) 182{ 183 for (int i = 0; i < TERMINAL_INPUT_ARG_COUNT; i++) { 184 args[i] = NULL; 185 } 186} 187 188/*! @brief Main terminal loop. */ 189void 190terminal_mainloop(void) 191{ 192 char inputBuffer[TERMINAL_INPUT_BUFFER_SIZE + 1]; 193 char *ptr; 194 int i; 195 196 while (!exitProgram) { 197 terminal_reset_input(); 198 terminal_get_input(inputBuffer); 199 200 ptr = strtok(inputBuffer, " "); 201 i = 0; 202 while (ptr != NULL) { 203 args[i] = ptr; 204 ptr = strtok(NULL, " "); 205 i++; 206 } 207 208 terminal_evaluate_command(inputBuffer); 209 } 210} 211 212/*! @brief Main terminal entry point. */ 213int main() 214{ 215 /* Future Work 3: 216 How the selfloader bootstraps user processes needs to be modified further. Changes were 217 made to accomodate the new way that muslc expects process's stacks to be set up when 218 processes start, but the one part of this that still needs to changed is how user processes 219 find their system call table. Currently the selfloader sets up user processes so that 220 the selfloader's system call table is used by user processes by passing the address of the 221 selfloader's system call table to the user processes via the user process's environment 222 variables. Ideally, user processes would use their own system call table. 223 */ 224 225 uintptr_t address = strtoll(getenv("SYSTABLE"), NULL, 16); 226 refos_init_selfload_child(address); 227 refos_initialise(); 228 terminal_startup_message(); 229 terminal_mainloop(); 230 printf("\n"); 231 return 0; 232} 233