1/* 2** Copyright 2004, Axel D��rfler, axeld@pinc-software.de. All rights reserved. 3** Distributed under the terms of the MIT License. 4*/ 5 6 7#include <stdlib.h> 8#include <string.h> 9 10#include <util/AutoLock.h> 11 12#include <team.h> 13 14#include "tty_private.h" 15 16 17//#define SLAVE_TRACE 18#ifdef SLAVE_TRACE 19# define TRACE(x) dprintf x 20#else 21# define TRACE(x) 22#endif 23 24 25struct slave_cookie : tty_cookie { 26}; 27 28 29struct tty gSlaveTTYs[kNumTTYs]; 30 31 32static status_t 33slave_open(const char *name, uint32 flags, void **_cookie) 34{ 35 // Get the tty index: Opening "/dev/tty" means opening the process' 36 // controlling tty. 37 int32 index = get_tty_index(name); 38 if (strcmp(name, "tty") == 0) { 39 index = team_get_controlling_tty(); 40 if (index < 0) 41 return B_NOT_ALLOWED; 42 } else { 43 index = get_tty_index(name); 44 if (index >= (int32)kNumTTYs) 45 return B_ERROR; 46 } 47 48 TRACE(("slave_open: TTY index = %" B_PRId32 " (name = %s)\n", index, 49 name)); 50 51 MutexLocker globalLocker(gGlobalTTYLock); 52 53 // we may only be used if our master has already been opened 54 if (gMasterTTYs[index].open_count == 0) 55 return B_IO_ERROR; 56 57 bool makeControllingTTY = (flags & O_NOCTTY) == 0; 58 pid_t processID = getpid(); 59 pid_t sessionID = getsid(processID); 60 61 if (gSlaveTTYs[index].open_count == 0) { 62 // We only allow session leaders to open the tty initially. 63 if (makeControllingTTY && processID != sessionID) 64 return B_NOT_ALLOWED; 65 66 status_t status = tty_open(&gSlaveTTYs[index], NULL); 67 if (status < B_OK) { 68 // initializing TTY failed 69 return status; 70 } 71 } else if (makeControllingTTY) { 72 // If already open, we allow only processes from the same session 73 // to open the tty again. 74 pid_t ttySession = gSlaveTTYs[index].settings->session_id; 75 if (ttySession >= 0) { 76 if (ttySession != sessionID) 77 return B_NOT_ALLOWED; 78 makeControllingTTY = false; 79 } else { 80 // The tty is not associated with a session yet. The process needs 81 // to be a session leader. 82 if (makeControllingTTY && processID != sessionID) 83 return B_NOT_ALLOWED; 84 } 85 } 86 87 slave_cookie *cookie = (slave_cookie *)malloc(sizeof(struct slave_cookie)); 88 if (cookie == NULL) { 89 if (gSlaveTTYs[index].open_count == 0) 90 tty_close(&gSlaveTTYs[index]); 91 92 return B_NO_MEMORY; 93 } 94 95 status_t status = init_tty_cookie(cookie, &gSlaveTTYs[index], 96 &gMasterTTYs[index], flags); 97 if (status != B_OK) { 98 free(cookie); 99 100 if (gSlaveTTYs[index].open_count == 0) 101 tty_close(&gSlaveTTYs[index]); 102 103 return status; 104 } 105 106 if (gSlaveTTYs[index].open_count == 0) { 107 gSlaveTTYs[index].lock = gMasterTTYs[index].lock; 108 gSlaveTTYs[index].settings->session_id = -1; 109 gSlaveTTYs[index].settings->pgrp_id = -1; 110 } 111 112 if (makeControllingTTY) { 113 gSlaveTTYs[index].settings->session_id = sessionID; 114 gSlaveTTYs[index].settings->pgrp_id = sessionID; 115 team_set_controlling_tty(gSlaveTTYs[index].index); 116 } 117 118 add_tty_cookie(cookie); 119 120 *_cookie = cookie; 121 122 return B_OK; 123} 124 125 126static status_t 127slave_close(void *_cookie) 128{ 129 slave_cookie *cookie = (slave_cookie *)_cookie; 130 131 TRACE(("slave_close: cookie %p\n", _cookie)); 132 133 MutexLocker globalLocker(gGlobalTTYLock); 134 135 // unblock and wait for all blocking operations 136 tty_close_cookie(cookie); 137 138 return B_OK; 139} 140 141 142static status_t 143slave_free_cookie(void *_cookie) 144{ 145 // The TTY is already closed. We only have to free the cookie. 146 slave_cookie *cookie = (slave_cookie *)_cookie; 147 148 TRACE(("slave_free_cookie: cookie %p\n", _cookie)); 149 150 MutexLocker globalLocker(gGlobalTTYLock); 151 uninit_tty_cookie(cookie); 152 globalLocker.Unlock(); 153 154 free(cookie); 155 156 return B_OK; 157} 158 159 160static status_t 161slave_ioctl(void *_cookie, uint32 op, void *buffer, size_t length) 162{ 163 slave_cookie *cookie = (slave_cookie *)_cookie; 164 165 TRACE(("slave_ioctl: cookie %p, op %" B_PRIu32 ", buffer %p, length %lu\n", 166 _cookie, op, buffer, length)); 167 168 return tty_ioctl(cookie, op, buffer, length); 169} 170 171 172static status_t 173slave_read(void *_cookie, off_t offset, void *buffer, size_t *_length) 174{ 175 slave_cookie *cookie = (slave_cookie *)_cookie; 176 177 TRACE(("slave_read: cookie %p, offset %" B_PRIdOFF ", buffer %p, length " 178 "%lu\n", _cookie, offset, buffer, *_length)); 179 180 status_t result = tty_input_read(cookie, buffer, _length); 181 182 TRACE(("slave_read done: cookie %p, result %" B_PRIx32 ", length %lu\n", 183 _cookie, result, *_length)); 184 185 return result; 186} 187 188 189static status_t 190slave_write(void *_cookie, off_t offset, const void *buffer, size_t *_length) 191{ 192 slave_cookie *cookie = (slave_cookie *)_cookie; 193 194 TRACE(("slave_write: cookie %p, offset %" B_PRIdOFF", buffer %p, length " 195 "%lu\n", _cookie, offset, buffer, *_length)); 196 197 status_t result = tty_write_to_tty_slave(cookie, buffer, _length); 198 199 TRACE(("slave_write done: cookie %p, result %" B_PRIx32 ", length %lu\n", 200 _cookie, result, *_length)); 201 202 return result; 203} 204 205 206static status_t 207slave_select(void *_cookie, uint8 event, uint32 ref, selectsync *sync) 208{ 209 slave_cookie *cookie = (slave_cookie *)_cookie; 210 211 return tty_select(cookie, event, ref, sync); 212} 213 214 215static status_t 216slave_deselect(void *_cookie, uint8 event, selectsync *sync) 217{ 218 slave_cookie *cookie = (slave_cookie *)_cookie; 219 220 return tty_deselect(cookie, event, sync); 221} 222 223 224device_hooks gSlaveTTYHooks = { 225 &slave_open, 226 &slave_close, 227 &slave_free_cookie, 228 &slave_ioctl, 229 &slave_read, 230 &slave_write, 231 &slave_select, 232 &slave_deselect, 233 NULL, // read_pages() 234 NULL // write_pages() 235}; 236