1/* 2** Copyright 2004, Axel Dörfler, axeld@pinc-software.de. All rights reserved. 3** Distributed under the terms of the Haiku 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 = %ld (name = %s)\n", index, name)); 49 50 MutexLocker globalLocker(gGlobalTTYLock); 51 52 // we may only be used if our master has already been opened 53 if (gMasterTTYs[index].open_count == 0) 54 return B_IO_ERROR; 55 56 bool makeControllingTTY = (flags & O_NOCTTY) == 0; 57 pid_t processID = getpid(); 58 pid_t sessionID = getsid(processID); 59 60 if (gSlaveTTYs[index].open_count == 0) { 61 // We only allow session leaders to open the tty initially. 62 if (makeControllingTTY && processID != sessionID) 63 return B_NOT_ALLOWED; 64 65 status_t status = tty_open(&gSlaveTTYs[index], NULL); 66 if (status < B_OK) { 67 // initializing TTY failed 68 return status; 69 } 70 } else if (makeControllingTTY) { 71 // If already open, we allow only processes from the same session 72 // to open the tty again. 73 pid_t ttySession = gSlaveTTYs[index].settings->session_id; 74 if (ttySession >= 0) { 75 if (ttySession != sessionID) 76 return B_NOT_ALLOWED; 77 makeControllingTTY = false; 78 } else { 79 // The tty is not associated with a session yet. The process needs 80 // to be a session leader. 81 if (makeControllingTTY && processID != sessionID) 82 return B_NOT_ALLOWED; 83 } 84 } 85 86 slave_cookie *cookie = (slave_cookie *)malloc(sizeof(struct slave_cookie)); 87 if (cookie == NULL) { 88 if (gSlaveTTYs[index].open_count == 0) 89 tty_close(&gSlaveTTYs[index]); 90 91 return B_NO_MEMORY; 92 } 93 94 status_t status = init_tty_cookie(cookie, &gSlaveTTYs[index], 95 &gMasterTTYs[index], flags); 96 if (status != B_OK) { 97 free(cookie); 98 99 if (gSlaveTTYs[index].open_count == 0) 100 tty_close(&gSlaveTTYs[index]); 101 102 return status; 103 } 104 105 if (gSlaveTTYs[index].open_count == 0) { 106 gSlaveTTYs[index].lock = gMasterTTYs[index].lock; 107 gSlaveTTYs[index].settings->session_id = -1; 108 gSlaveTTYs[index].settings->pgrp_id = -1; 109 } 110 111 if (makeControllingTTY) { 112 gSlaveTTYs[index].settings->session_id = sessionID; 113 gSlaveTTYs[index].settings->pgrp_id = sessionID; 114 team_set_controlling_tty(gSlaveTTYs[index].index); 115 } 116 117 add_tty_cookie(cookie); 118 119 *_cookie = cookie; 120 121 return B_OK; 122} 123 124 125static status_t 126slave_close(void *_cookie) 127{ 128 slave_cookie *cookie = (slave_cookie *)_cookie; 129 130 TRACE(("slave_close: cookie %p\n", _cookie)); 131 132 MutexLocker globalLocker(gGlobalTTYLock); 133 134 // unblock and wait for all blocking operations 135 tty_close_cookie(cookie); 136 137 return B_OK; 138} 139 140 141static status_t 142slave_free_cookie(void *_cookie) 143{ 144 // The TTY is already closed. We only have to free the cookie. 145 slave_cookie *cookie = (slave_cookie *)_cookie; 146 147 TRACE(("slave_free_cookie: cookie %p\n", _cookie)); 148 149 MutexLocker globalLocker(gGlobalTTYLock); 150 uninit_tty_cookie(cookie); 151 globalLocker.Unlock(); 152 153 free(cookie); 154 155 return B_OK; 156} 157 158 159static status_t 160slave_ioctl(void *_cookie, uint32 op, void *buffer, size_t length) 161{ 162 slave_cookie *cookie = (slave_cookie *)_cookie; 163 164 TRACE(("slave_ioctl: cookie %p, op %lu, buffer %p, length %lu\n", _cookie, op, buffer, length)); 165 166 return tty_ioctl(cookie, op, buffer, length); 167} 168 169 170static status_t 171slave_read(void *_cookie, off_t offset, void *buffer, size_t *_length) 172{ 173 slave_cookie *cookie = (slave_cookie *)_cookie; 174 175 TRACE(("slave_read: cookie %p, offset %Ld, buffer %p, length %lu\n", 176 _cookie, offset, buffer, *_length)); 177 178 status_t result = tty_input_read(cookie, buffer, _length); 179 180 TRACE(("slave_read done: cookie %p, result %lx, length %lu\n", 181 _cookie, result, *_length)); 182 183 return result; 184} 185 186 187static status_t 188slave_write(void *_cookie, off_t offset, const void *buffer, size_t *_length) 189{ 190 slave_cookie *cookie = (slave_cookie *)_cookie; 191 192 TRACE(("slave_write: cookie %p, offset %Ld, buffer %p, length %lu\n", 193 _cookie, offset, buffer, *_length)); 194 195 status_t result = tty_write_to_tty_slave(cookie, buffer, _length); 196 197 TRACE(("slave_write done: cookie %p, result %lx, length %lu\n", 198 _cookie, result, *_length)); 199 200 return result; 201} 202 203 204static status_t 205slave_select(void *_cookie, uint8 event, uint32 ref, selectsync *sync) 206{ 207 slave_cookie *cookie = (slave_cookie *)_cookie; 208 209 return tty_select(cookie, event, ref, sync); 210} 211 212 213static status_t 214slave_deselect(void *_cookie, uint8 event, selectsync *sync) 215{ 216 slave_cookie *cookie = (slave_cookie *)_cookie; 217 218 return tty_deselect(cookie, event, sync); 219} 220 221 222device_hooks gSlaveTTYHooks = { 223 &slave_open, 224 &slave_close, 225 &slave_free_cookie, 226 &slave_ioctl, 227 &slave_read, 228 &slave_write, 229 &slave_select, 230 &slave_deselect, 231 NULL, // read_pages() 232 NULL // write_pages() 233}; 234