1 /* 2 * MiniTerminal - A basic windowed terminal to allow 3 * command-line interaction from within app_server. 4 * Based on consoled and MuTerminal. 5 * 6 * Copyright 2005 Michael Lotz. All rights reserved. 7 * Distributed under the Haiku License. 8 * 9 * Copyright 2004-2005, Haiku. All rights reserved. 10 * Distributed under the terms of the MIT License. 11 * 12 * Copyright 2002, Travis Geiselbrecht. All rights reserved. 13 * Distributed under the terms of the NewOS License. 14 */ 15 16 17 #include <OS.h> 18 #include <image.h> 19 20 #include <termios.h> 21 #include <unistd.h> 22 #include <dirent.h> 23 #include <string.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <signal.h> 27 28 #include "MiniView.h" 29 #include "Console.h" 30 31 #include "VTkeymap.h" 32 33 //#define TRACE_MINI_TERMINAL 34 #ifdef TRACE_MINI_TERMINAL 35 #ifdef __HAIKU__ 36 #define TRACE(x) debug_printf x 37 #else 38 #define TRACE(x) printf x 39 #endif 40 #else 41 #define TRACE(x) 42 #endif 43 44 void 45 Setenv(const char *var, const char *value) 46 { 47 int envindex = 0; 48 const int len = strlen(var); 49 const int val_len = strlen (value); 50 51 while (environ [envindex] != NULL) { 52 if (strncmp (environ [envindex], var, len) == 0) { 53 /* found it */ 54 environ[envindex] = (char *)malloc ((unsigned)len + val_len + 1); 55 sprintf (environ [envindex], "%s%s", var, value); 56 return; 57 } 58 envindex ++; 59 } 60 61 environ [envindex] = (char *) malloc ((unsigned)len + val_len + 1); 62 sprintf (environ [envindex], "%s%s", var, value); 63 environ [++envindex] = NULL; 64 } 65 66 static int32 67 ConsoleWriter(void *arg) 68 { 69 char buf[1024]; 70 ssize_t len; 71 MiniView *view = (MiniView *)arg; 72 73 for (;;) { 74 len = read(view->fMasterFD, buf, sizeof(buf)); 75 if (len < 0) 76 break; 77 78 view->fConsole->Write(buf, len); 79 } 80 81 return 0; 82 } 83 84 static int32 85 ExecuteShell(void *arg) 86 { 87 MiniView *view = (MiniView *)arg; 88 89 for (;;) { 90 const char *argv[3]; 91 argv[0] = "/bin/sh"; 92 argv[1] = "--login"; 93 argv[2] = NULL; 94 95 int saved_stdin = dup(0); 96 int saved_stdout = dup(1); 97 int saved_stderr = dup(2); 98 99 dup2(view->fSlaveFD, 0); 100 dup2(view->fSlaveFD, 1); 101 dup2(view->fSlaveFD, 2); 102 103 thread_id shell = load_image(2, argv, (const char **)environ); 104 setpgid(shell, 0); 105 106 status_t return_code; 107 wait_for_thread(shell, &return_code); 108 109 dup2(saved_stdin, 0); 110 dup2(saved_stdout, 1); 111 dup2(saved_stderr, 2); 112 close(saved_stdin); 113 close(saved_stdout); 114 close(saved_stderr); 115 } 116 117 return B_OK; 118 } 119 120 MiniView::MiniView(BRect frame) 121 : ViewBuffer(frame) 122 { 123 } 124 125 MiniView::~MiniView() 126 { 127 } 128 129 void 130 MiniView::Start() 131 { 132 fConsole = new Console(this); 133 134 if (OpenTTY() != B_OK) { 135 TRACE(("error in OpenTTY\n")); 136 return; 137 } 138 139 // we're a session leader 140 setsid(); 141 142 // move our stdin and stdout to the console 143 dup2(fSlaveFD, 0); 144 dup2(fSlaveFD, 1); 145 dup2(fSlaveFD, 2); 146 147 if (SpawnThreads() != B_OK) 148 TRACE(("error in SpawnThreads\n")); 149 } 150 151 status_t 152 MiniView::OpenTTY() 153 { 154 DIR *dir; 155 156 dir = opendir("/dev/pt"); 157 if (dir != NULL) { 158 struct dirent *entry; 159 char name[64]; 160 161 while ((entry = readdir(dir)) != NULL) { 162 if (entry->d_name[0] == '.') // filter out . and .. 163 continue; 164 165 sprintf(name, "/dev/pt/%s", entry->d_name); 166 167 fMasterFD = open(name, O_RDWR); 168 if (fMasterFD >= 0) { 169 sprintf(name, "/dev/tt/%s", entry->d_name); 170 171 fSlaveFD = open(name, O_RDWR); 172 if (fSlaveFD < 0) { 173 TRACE(("cannot open tty\n")); 174 close(fMasterFD); 175 } else { 176 struct termios tio; 177 tcgetattr(fSlaveFD, &tio); 178 179 // set signal default 180 signal(SIGCHLD, SIG_DFL); 181 signal(SIGHUP, SIG_DFL); 182 signal(SIGQUIT, SIG_DFL); 183 signal(SIGTERM, SIG_DFL); 184 signal(SIGINT, SIG_DFL); 185 signal(SIGTTOU, SIG_DFL); 186 187 // set terminal interface 188 tio.c_line = 0; 189 tio.c_lflag |= ECHOE; 190 191 // input: nl->nl, cr->nl 192 tio.c_iflag &= ~(INLCR|IGNCR); 193 tio.c_iflag |= ICRNL; 194 tio.c_iflag &= ~ISTRIP; 195 196 // output: cr->cr, nl in not retrun, no delays, ln->cr/ln 197 tio.c_oflag &= ~(OCRNL|ONLRET|NLDLY|CRDLY|TABDLY|BSDLY|VTDLY|FFDLY); 198 tio.c_oflag |= ONLCR; 199 tio.c_oflag |= OPOST; 200 201 // baud rate is 19200 (equal to beterm) 202 tio.c_cflag &= ~(CBAUD); 203 tio.c_cflag |= B19200; 204 205 tio.c_cflag &= ~CSIZE; 206 tio.c_cflag |= CS8; 207 tio.c_cflag |= CREAD; 208 209 tio.c_cflag |= HUPCL; 210 tio.c_iflag &= ~(IGNBRK|BRKINT); 211 212 // enable signals, canonical processing (erase, kill, etc), echo 213 tio.c_lflag |= ISIG|ICANON|ECHO|ECHOE|ECHONL; 214 tio.c_lflag &= ~ECHOK; 215 216 tio.c_lflag &= ~IEXTEN; 217 218 // set control charactors 219 tio.c_cc[VINTR] = 'C' & 0x1f; /* '^C' */ 220 tio.c_cc[VQUIT] = '\\'& 0x1f; /* '^\' */ 221 tio.c_cc[VERASE] = 0x08; /* '^H' */ 222 tio.c_cc[VKILL] = 'U' & 0x1f; /* '^U' */ 223 tio.c_cc[VEOF] = 'D' & 0x1f; /* '^D' */ 224 tio.c_cc[VEOL] = 0; /* '^@' */ 225 tio.c_cc[VMIN] = 4; 226 tio.c_cc[VTIME] = 0; 227 tio.c_cc[VEOL2] = 0; /* '^@' */ 228 tio.c_cc[VSWTCH] = 0; /* '^@' */ 229 tio.c_cc[VSTART] = 'S' & 0x1f; /* '^S' */ 230 tio.c_cc[VSTOP] = 'Q' & 0x1f; /* '^Q' */ 231 tio.c_cc[VSUSP] = '@' & 0x1f; /* '^@' */ 232 233 // set terminal interface 234 tcsetattr(fSlaveFD, TCSANOW, &tio); 235 236 // set window size (currently disabled) 237 /*ws.ws_row = rows; 238 ws.ws_col = cols; 239 240 ioctl(con->tty_slave_fd, TIOCSWINSZ, &ws);*/ 241 } 242 break; 243 } 244 } 245 246 247 Setenv("TTY", name); 248 } 249 250 if (fMasterFD < 0 || fSlaveFD < 0) { 251 TRACE(("could not open master or slave fd\n")); 252 return B_ERROR; 253 } 254 255 Setenv("TERM", "beterm"); 256 return B_OK; 257 } 258 259 void 260 MiniView::KeyDown(const char *bytes, int32 numBytes) 261 { 262 // TODO: add interrupt char handling 263 uint32 mod = modifiers(); 264 if (numBytes == 1) { 265 if (mod & B_OPTION_KEY) { 266 char c = bytes[0] | 0x80; 267 write(fMasterFD, &c, 1); 268 } else { 269 switch (bytes[0]) { 270 case B_LEFT_ARROW: 271 write(fMasterFD, LEFT_ARROW_KEY_CODE, sizeof(LEFT_ARROW_KEY_CODE)); 272 break; 273 case B_RIGHT_ARROW: 274 write(fMasterFD, RIGHT_ARROW_KEY_CODE, sizeof(RIGHT_ARROW_KEY_CODE)); 275 break; 276 case B_UP_ARROW: 277 write(fMasterFD, UP_ARROW_KEY_CODE, sizeof(UP_ARROW_KEY_CODE)); 278 break; 279 case B_DOWN_ARROW: 280 write(fMasterFD, DOWN_ARROW_KEY_CODE, sizeof(DOWN_ARROW_KEY_CODE)); 281 break; 282 default: 283 write(fMasterFD, bytes, numBytes); 284 } 285 } 286 } else 287 write(fMasterFD, bytes, numBytes); 288 } 289 290 status_t 291 MiniView::SpawnThreads() 292 { 293 fConsoleWriter = spawn_thread(&ConsoleWriter, "console writer", B_URGENT_DISPLAY_PRIORITY, this); 294 if (fConsoleWriter < 0) 295 return B_ERROR; 296 TRACE(("console writer thread is: %d\n", fConsoleWriter)); 297 298 fShellProcess = spawn_thread(&ExecuteShell, "shell process", B_URGENT_DISPLAY_PRIORITY, this); 299 if (fShellProcess < 0) 300 return B_ERROR; 301 TRACE(("shell process thread is: %d\n", fShellProcess)); 302 303 resume_thread(fConsoleWriter); 304 resume_thread(fShellProcess); 305 return B_OK; 306 } 307