1 /* 2 * Copyright 2001-2005, Haiku. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Pahtz <pahtz@yahoo.com.au> 7 * Axel Dörfler 8 */ 9 10 /** Class for low-overhead port-based messaging */ 11 12 #include <stdlib.h> 13 #include <string.h> 14 #include <new> 15 16 #include <ServerProtocol.h> 17 #include <LinkReceiver.h> 18 #include <String.h> 19 20 #include "link_message.h" 21 22 23 //#define DEBUG_BPORTLINK 24 #ifdef DEBUG_BPORTLINK 25 # include <stdio.h> 26 # define STRACE(x) printf x 27 #else 28 # define STRACE(x) ; 29 #endif 30 31 namespace BPrivate { 32 33 LinkReceiver::LinkReceiver(port_id port) 34 : 35 fReceivePort(port), fRecvBuffer(NULL), fRecvPosition(0), fRecvStart(0), 36 fRecvBufferSize(0), fDataSize(0), 37 fReplySize(0), fReadError(B_OK) 38 { 39 } 40 41 42 LinkReceiver::~LinkReceiver() 43 { 44 free(fRecvBuffer); 45 } 46 47 48 void 49 LinkReceiver::SetPort(port_id port) 50 { 51 fReceivePort = port; 52 } 53 54 55 status_t 56 LinkReceiver::GetNextMessage(int32 &code, bigtime_t timeout) 57 { 58 int32 remaining; 59 60 fReadError = B_OK; 61 62 remaining = fDataSize - (fRecvStart + fReplySize); 63 STRACE(("info: LinkReceiver GetNextReply() reports %ld bytes remaining in buffer.\n", remaining)); 64 65 // find the position of the next message header in the buffer 66 message_header *header; 67 if (remaining <= 0) { 68 status_t err = ReadFromPort(timeout); 69 if (err < B_OK) 70 return err; 71 remaining = fDataSize; 72 header = (message_header *)fRecvBuffer; 73 } else { 74 fRecvStart += fReplySize; // start of the next message 75 fRecvPosition = fRecvStart; 76 header = (message_header *)(fRecvBuffer + fRecvStart); 77 } 78 79 // check we have a well-formed message 80 if (remaining < (int32)sizeof(message_header)) { 81 // we don't have enough data for a complete header 82 STRACE(("error info: LinkReceiver remaining %ld bytes is less than header size.\n", remaining)); 83 ResetBuffer(); 84 return B_ERROR; 85 } 86 87 fReplySize = header->size; 88 if (fReplySize > remaining || fReplySize < (int32)sizeof(message_header)) { 89 STRACE(("error info: LinkReceiver message size of %ld bytes smaller than header size.\n", fReplySize)); 90 ResetBuffer(); 91 return B_ERROR; 92 } 93 94 code = header->code; 95 fRecvPosition += sizeof(message_header); 96 97 STRACE(("info: LinkReceiver got header %ld [%ld %ld %ld] from port %ld.\n", 98 header->code, fReplySize, header->code, header->flags, fReceivePort)); 99 100 return B_OK; 101 } 102 103 104 bool 105 LinkReceiver::HasMessages() const 106 { 107 return fDataSize - (fRecvStart + fReplySize) > 0 108 || port_count(fReceivePort) > 0; 109 } 110 111 112 bool 113 LinkReceiver::NeedsReply() const 114 { 115 if (fReplySize == 0) 116 return false; 117 118 message_header *header = (message_header *)(fRecvBuffer + fRecvStart); 119 return (header->flags & kNeedsReply) != 0; 120 } 121 122 123 int32 124 LinkReceiver::Code() const 125 { 126 if (fReplySize == 0) 127 return B_ERROR; 128 129 message_header *header = (message_header *)(fRecvBuffer + fRecvStart); 130 return header->code; 131 } 132 133 134 void 135 LinkReceiver::ResetBuffer() 136 { 137 fRecvPosition = 0; 138 fRecvStart = 0; 139 fDataSize = 0; 140 fReplySize = 0; 141 } 142 143 144 status_t 145 LinkReceiver::AdjustReplyBuffer(bigtime_t timeout) 146 { 147 // Here we take advantage of the compiler's dead-code elimination 148 if (kInitialBufferSize == kMaxBufferSize) { 149 // fixed buffer size 150 151 if (fRecvBuffer != NULL) 152 return B_OK; 153 154 fRecvBuffer = (char *)malloc(kInitialBufferSize); 155 if (fRecvBuffer == NULL) 156 return B_NO_MEMORY; 157 158 fRecvBufferSize = kInitialBufferSize; 159 } else { 160 STRACE(("info: LinkReceiver getting port_buffer_size().\n")); 161 ssize_t bufferSize; 162 if (timeout == B_INFINITE_TIMEOUT) 163 bufferSize = port_buffer_size(fReceivePort); 164 else 165 bufferSize = port_buffer_size_etc(fReceivePort, B_TIMEOUT, timeout); 166 STRACE(("info: LinkReceiver got port_buffer_size() = %ld.\n", bufferSize)); 167 168 if (bufferSize < 0) 169 return (status_t)bufferSize; 170 171 // make sure our receive buffer is large enough 172 if (bufferSize > fRecvBufferSize) { 173 if (bufferSize <= (ssize_t)kInitialBufferSize) 174 bufferSize = (ssize_t)kInitialBufferSize; 175 else 176 bufferSize = (bufferSize + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1); 177 178 if (bufferSize > (ssize_t)kMaxBufferSize) 179 return B_ERROR; // we can't continue 180 181 STRACE(("info: LinkReceiver setting receive buffersize to %ld.\n", bufferSize)); 182 char *buffer = (char *)malloc(bufferSize); 183 if (buffer == NULL) 184 return B_NO_MEMORY; 185 186 free(fRecvBuffer); 187 fRecvBuffer = buffer; 188 fRecvBufferSize = bufferSize; 189 } 190 } 191 192 return B_OK; 193 } 194 195 196 status_t 197 LinkReceiver::ReadFromPort(bigtime_t timeout) 198 { 199 // we are here so it means we finished reading the buffer contents 200 ResetBuffer(); 201 202 status_t err = AdjustReplyBuffer(timeout); 203 if (err < B_OK) 204 return err; 205 206 int32 code; 207 ssize_t bytesRead; 208 209 STRACE(("info: LinkReceiver reading port %ld.\n", fReceivePort)); 210 while (true) { 211 if (timeout != B_INFINITE_TIMEOUT) { 212 do { 213 bytesRead = read_port_etc(fReceivePort, &code, fRecvBuffer, 214 fRecvBufferSize, B_TIMEOUT, timeout); 215 } while (bytesRead == B_INTERRUPTED); 216 } else { 217 do { 218 bytesRead = read_port(fReceivePort, &code, fRecvBuffer, 219 fRecvBufferSize); 220 } while (bytesRead == B_INTERRUPTED); 221 } 222 223 STRACE(("info: LinkReceiver read %ld bytes.\n", bytesRead)); 224 if (bytesRead < B_OK) 225 return bytesRead; 226 227 // we just ignore incorrect messages, and don't bother our caller 228 229 if (code != kLinkCode) { 230 STRACE(("wrong port message %lx received.\n", code)); 231 continue; 232 } 233 234 // port read seems to be valid 235 break; 236 } 237 238 fDataSize = bytesRead; 239 return B_OK; 240 } 241 242 243 status_t 244 LinkReceiver::Read(void *data, ssize_t size) 245 { 246 // STRACE(("info: LinkReceiver Read()ing %ld bytes...\n", size)); 247 if (fReadError < B_OK) 248 return fReadError; 249 250 if (data == NULL || size < 1) { 251 fReadError = B_BAD_VALUE; 252 return B_BAD_VALUE; 253 } 254 255 if (fDataSize == 0 || fReplySize == 0) 256 return B_NO_INIT; // need to call GetNextReply() first 257 258 if (fRecvPosition + size > fRecvStart + fReplySize) { 259 // reading past the end of current message 260 fReadError = B_BAD_VALUE; 261 return B_BAD_VALUE; 262 } 263 264 memcpy(data, fRecvBuffer + fRecvPosition, size); 265 fRecvPosition += size; 266 return fReadError; 267 } 268 269 270 status_t 271 LinkReceiver::ReadString(char **_string) 272 { 273 int32 length = 0; 274 status_t status; 275 276 status = Read<int32>(&length); 277 if (status < B_OK) 278 return status; 279 280 if (length >= 0) { 281 char *string = (char *)malloc(length + 1); 282 if (string == NULL) { 283 fRecvPosition -= sizeof(int32); // rewind the transaction 284 return B_NO_MEMORY; 285 } 286 287 if (length > 0) { 288 status = Read(string, length); 289 if (status < B_OK) { 290 free(string); 291 fRecvPosition -= sizeof(int32); // rewind the transaction 292 return status; 293 } 294 } 295 296 // make sure the string is null terminated 297 string[length] = '\0'; 298 299 *_string = string; 300 return B_OK; 301 } else { 302 fRecvPosition -= sizeof(int32); // rewind the transaction 303 return B_ERROR; 304 } 305 } 306 307 308 status_t 309 LinkReceiver::ReadString(BString &string) 310 { 311 int32 length = 0; 312 status_t status; 313 314 status = Read<int32>(&length); 315 if (status < B_OK) 316 return status; 317 318 if (length < 0) { 319 status = B_ERROR; 320 goto err; 321 } 322 323 if (length > 0) { 324 char* buffer = string.LockBuffer(length + 1); 325 if (buffer == NULL) { 326 status = B_NO_MEMORY; 327 goto err; 328 } 329 330 status = Read(buffer, length); 331 if (status < B_OK) { 332 string.UnlockBuffer(); 333 goto err; 334 } 335 336 // make sure the string is null terminated 337 buffer[length] = '\0'; 338 string.UnlockBuffer(length); 339 } else 340 string = ""; 341 342 return B_OK; 343 344 err: 345 fRecvPosition -= sizeof(int32); 346 // rewind the transaction 347 return status; 348 } 349 350 351 status_t 352 LinkReceiver::ReadString(char *buffer, size_t bufferLength) 353 { 354 int32 length = 0; 355 status_t status; 356 357 status = Read<int32>(&length); 358 if (status < B_OK) 359 return status; 360 361 if (length >= (int32)bufferLength) { 362 status = B_BUFFER_OVERFLOW; 363 goto err; 364 } 365 366 if (length < 0) { 367 status = B_ERROR; 368 goto err; 369 } 370 371 if (length > 0) { 372 status = Read(buffer, length); 373 if (status < B_OK) 374 goto err; 375 } 376 377 // make sure the string is null terminated 378 buffer[length] = '\0'; 379 return B_OK; 380 381 err: 382 fRecvPosition -= sizeof(int32); 383 // rewind the transaction 384 return status; 385 } 386 387 } // namespace BPrivate 388