1/* 2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24#include "webdavd.h" 25#include "LogMessage.h" 26 27#include <sys/syslog.h> 28#include <err.h> 29#include <string.h> 30#include <stdlib.h> 31#include <stdio.h> 32#include <sys/param.h> 33#include <sys/socket.h> 34#include <sys/uio.h> 35#include <sys/sysctl.h> 36#include "webdav_requestqueue.h" 37#include "webdav_network.h" 38#include "webdav_cookie.h" 39 40/*****************************************************************************/ 41 42extern fsid_t g_fsid; /* file system id */ 43extern int g_vfc_typenum; 44extern char g_mountPoint[MAXPATHLEN]; /* path to our mount point */ 45 46/* structure */ 47 48typedef struct webdav_requestqueue_element_tag 49{ 50 struct webdav_requestqueue_element_tag *next; 51 int type; 52 union 53 { 54 struct request 55 { 56 int socket; /* socket for connection */ 57 } request; /* Struct used for requests from the kernel */ 58 59 struct download 60 { 61 struct node_entry *node; /* the node */ 62 struct ReadStreamRec *readStreamRecPtr; /* the ReadStreamRec */ 63 } download; /* Struct used for download requests */ 64 65 struct serverping 66 { 67 u_int32_t delay; /* used for backoff delay sending ping requests to the server */ 68 } serverping; 69 70 struct seqwrite_read_rsp 71 { 72 struct stream_put_ctx *ctx; 73 } seqwrite_read_rsp; 74 75 } element; 76} webdav_requestqueue_element_t; 77 78typedef struct 79{ 80 webdav_requestqueue_element_t *item_head; 81 webdav_requestqueue_element_t *item_tail; 82 int request_count; 83} webdav_requestqueue_header_t; 84 85/*****************************************************************************/ 86 87/* Definitions */ 88#define WEBDAV_REQUEST_TYPE 1 89#define WEBDAV_DOWNLOAD_TYPE 2 90#define WEBDAV_SERVER_PING_TYPE 3 91#define WEBDAV_SEQWRITE_MANAGER_TYPE 4 92 93#define WEBDAV_MAX_IDLE_TIME 10 /* in seconds */ 94 95 96/* connectionstate_lock used to make connectionstate thread safe */ 97static pthread_mutex_t connectionstate_lock; 98/* connectionstate is set to either WEBDAV_CONNECTION_UP or WEBDAV_CONNECTION_DOWN */ 99static int connectionstate; 100 101static pthread_mutex_t requests_lock; 102static pthread_cond_t requests_condvar; 103static webdav_requestqueue_header_t waiting_requests; 104 105static pthread_mutex_t pulse_lock; 106static pthread_cond_t pulse_condvar; 107static int purge_cache_files; /* TRUE if closed cache files should be immediately removed from file cache */ 108 109static int handle_request_thread(void *arg); 110 111static int gCurrThreadCount = 0; 112static int gIdleThreadCount = 0; 113static pthread_attr_t gRequest_thread_attr; 114 115 116/*****************************************************************************/ 117 118/* get the connectionstate */ 119int get_connectionstate(void) 120{ 121 int error; 122 int result = 1; /* return bad if we cannot lock the mutex */ 123 124 error = pthread_mutex_lock(&connectionstate_lock); 125 require_noerr(error, pthread_mutex_lock); 126 127 result = connectionstate; 128 129 error = pthread_mutex_unlock(&connectionstate_lock); 130 require_noerr(error, pthread_mutex_unlock); 131 132pthread_mutex_unlock: 133pthread_mutex_lock: 134 135 return ( result ); 136} 137 138static void notify_reconnected(void) 139{ 140 int mib[5]; 141 struct statfs *buf; 142 int i, count; 143 size_t len; 144 145 // lazily fetch our g_fsid 146 if ( g_fsid.val[0] == -1 && g_fsid.val[1] == -1) { 147 // Fetch mounted filesystem stats. Specify the MNT_NOWAIT flag to directly return the information 148 // retained in the kernel to avoid delays caused by waiting 149 // for updated information from a file system. 150 count = getmntinfo(&buf, MNT_NOWAIT); 151 if (!count) { 152 syslog(LOG_DEBUG, "%s: errno %d fetching mnt info", __FUNCTION__, errno); 153 return; 154 } 155 156 len = (unsigned int)strlen(g_mountPoint); 157 for (i = 0; i < count; i++) 158 { 159 if ( (strcmp("webdav", buf[i].f_fstypename) == 0) && 160 (strlen(buf[i].f_mntonname) == len) && 161 (strncasecmp(buf[i].f_mntonname, g_mountPoint, len) == 0) ) { 162 // found our fs 163 g_fsid = buf[i].f_fsid; 164 break; 165 } 166 } 167 } 168 169 if ( g_fsid.val[0] != -1 && g_fsid.val[1] != -1 ) { 170 /* setup mib for the request */ 171 mib[0] = CTL_VFS; 172 mib[1] = g_vfc_typenum; 173 mib[2] = WEBDAV_NOTIFY_RECONNECTED_SYSCTL; 174 mib[3] = g_fsid.val[0]; // fsid byte 0 of reconnected file system 175 mib[4] = g_fsid.val[1]; // fsid byte 1 of reconnected file system 176 177 if (sysctl(mib, 5, NULL, NULL, NULL, 0) != 0) 178 syslog(LOG_ERR, "%s: sysctl errno %d", __FUNCTION__, errno ); 179 } 180 else { 181 syslog(LOG_DEBUG, "%s: fsid not found for %s\n", __FUNCTION__, g_mountPoint); 182 } 183} 184 185/*****************************************************************************/ 186 187/* set the connectionstate */ 188void set_connectionstate(int state) 189{ 190 int error; 191 192 error = pthread_mutex_lock(&connectionstate_lock); 193 require_noerr(error, pthread_mutex_lock); 194 195 switch (state) { 196 case WEBDAV_CONNECTION_DOWN: 197 if (connectionstate == WEBDAV_CONNECTION_UP) { 198 199 syslog(LOG_ERR, "WebDAV server is no longer responding, will keep retrying..."); 200 201 /* transition to DOWN state */ 202 connectionstate = WEBDAV_CONNECTION_DOWN; 203 204 /* start pinging the server, specifying 0 delay for the 1st ping */ 205 requestqueue_enqueue_server_ping(0); 206 } 207 break; 208 case WEBDAV_CONNECTION_UP: 209 if (connectionstate == WEBDAV_CONNECTION_DOWN) { 210 syslog(LOG_ERR, "WebDAV server is now responding normally"); 211 notify_reconnected(); // let the kext know the server is online 212 connectionstate = WEBDAV_CONNECTION_UP; 213 } 214 break; 215 216 default: 217 break; 218 } 219 220 error = pthread_mutex_unlock(&connectionstate_lock); 221 require_noerr(error, pthread_mutex_unlock); 222 223pthread_mutex_unlock: 224pthread_mutex_lock: 225 226 return; 227} 228 229/*****************************************************************************/ 230 231static int get_request(int so, int *operation, void *key, size_t klen) 232{ 233 int error; 234 struct iovec iov[2]; 235 struct msghdr msg; 236 ssize_t n; 237 238 iov[0].iov_base = (caddr_t)operation; 239 iov[0].iov_len = sizeof(int); 240 iov[1].iov_base = key; 241 iov[1].iov_len = klen; 242 243 memset(&msg, 0, sizeof(msg)); 244 msg.msg_iov = iov; 245 msg.msg_iovlen = 2; 246 247 n = recvmsg(so, &msg, 0); 248 249 if ( n >= (int)(sizeof(int) + sizeof(struct webdav_cred)) ) 250 { 251 /* the message received is large enough to contain operation and webdav_cred */ 252 error = 0; 253 /* terminate the string (if any) at the end of the key */ 254 n -= sizeof(int); 255 ((char *)key)[n] = '\0'; 256 } 257 else if ( n < 0 ) 258 { 259 /* error from recvmsg */ 260 error = errno; 261 LogMessage(kError, "get_request recvmsg failed error %d\n", error); 262 } 263 else 264 { 265 /* the message was too short */ 266 error = EINVAL; 267 LogMessage(kError, "get_request got short message\n"); 268 } 269 270 return ( error ); 271} 272 273/*****************************************************************************/ 274 275static void send_reply(int so, void *data, size_t size, int error) 276{ 277 ssize_t n; 278 struct iovec iov[2]; 279 struct msghdr msg; 280 int send_error = error; 281 282 /* if the connection is down, let the kernel know */ 283 if ( get_connectionstate() == WEBDAV_CONNECTION_DOWN ) 284 { 285 send_error |= WEBDAV_CONNECTION_DOWN_MASK; 286 } 287 288 iov[0].iov_base = (caddr_t)&send_error; 289 iov[0].iov_len = sizeof(send_error); 290 if ( size != 0 ) 291 { 292 iov[1].iov_base = (caddr_t)data; 293 iov[1].iov_len = size; 294 } 295 296 memset(&msg, 0, sizeof(msg)); 297 msg.msg_iov = iov; 298 299 if ( size != 0 ) 300 { 301 msg.msg_iovlen = 2; 302 } 303 else 304 { 305 msg.msg_iovlen = 1; 306 } 307 308 n = sendmsg(so, &msg, 0); 309 if (n < 0) 310 { 311 LogMessage(kError, "send_reply sendmsg failed\n"); 312 } 313} 314 315/*****************************************************************************/ 316 317static void handle_filesystem_request(int so) 318{ 319 int error; 320 int operation; 321 char key[(NAME_MAX + 1) + sizeof(union webdav_request)]; 322 size_t num_bytes; 323 char *bytes; 324 union webdav_reply reply; 325 326 /* get the request from the socket */ 327 error = get_request(so, &operation, key, sizeof(key)); 328 if ( !error ) { 329#if DEBUG 330 LogMessage(kTrace, "handle_filesystem_request: %s(%d)\n", 331 (operation==WEBDAV_LOOKUP) ? "LOOKUP" : 332 (operation==WEBDAV_CREATE) ? "CREATE" : 333 (operation==WEBDAV_OPEN) ? "OPEN" : 334 (operation==WEBDAV_CLOSE) ? "CLOSE" : 335 (operation==WEBDAV_GETATTR) ? "GETATTR" : 336 (operation==WEBDAV_SETATTR) ? "SETATTR" : 337 (operation==WEBDAV_READ) ? "READ" : 338 (operation==WEBDAV_WRITE) ? "WRITE" : 339 (operation==WEBDAV_FSYNC) ? "FSYNC" : 340 (operation==WEBDAV_REMOVE) ? "REMOVE" : 341 (operation==WEBDAV_RENAME) ? "RENAME" : 342 (operation==WEBDAV_MKDIR) ? "MKDIR" : 343 (operation==WEBDAV_RMDIR) ? "RMDIR" : 344 (operation==WEBDAV_READDIR) ? "READDIR" : 345 (operation==WEBDAV_STATFS) ? "STATFS" : 346 (operation==WEBDAV_UNMOUNT) ? "UNMOUNT" : 347 (operation==WEBDAV_INVALCACHES) ? "INVALCACHES" : 348 "???", 349 operation 350 ); 351#endif 352 bzero((void *)&reply, sizeof(union webdav_reply)); 353 354 /* If the connection is down just return EBUSY, but always let UNMOUNT and INVALCACHES requests */ 355 /* go through regardless of the state of the connection. */ 356 if ( (get_connectionstate() == WEBDAV_CONNECTION_DOWN) && (operation != WEBDAV_UNMOUNT) && 357 (operation != WEBDAV_INVALCACHES) ) 358 { 359 error = ETIMEDOUT; 360 send_reply(so, (void *)&reply, sizeof(union webdav_reply), error); 361 } 362 else 363 { 364 /* call the function to handle the request */ 365 switch ( operation ) 366 { 367 case WEBDAV_LOOKUP: 368 error = filesystem_lookup((struct webdav_request_lookup *)key, 369 (struct webdav_reply_lookup *)&reply); 370 send_reply(so, (void *)&reply, sizeof(struct webdav_reply_lookup), error); 371 break; 372 373 case WEBDAV_CREATE: 374 error = filesystem_create((struct webdav_request_create *)key, 375 (struct webdav_reply_create *)&reply); 376 send_reply(so, (void *)&reply, sizeof(struct webdav_reply_create), error); 377 break; 378 379 case WEBDAV_OPEN: 380 error = filesystem_open((struct webdav_request_open *)key, 381 (struct webdav_reply_open *)&reply); 382 send_reply(so, (void *)&reply, sizeof(struct webdav_reply_open), error); 383 break; 384 385 case WEBDAV_CLOSE: 386 error = filesystem_close((struct webdav_request_close *)key); 387 send_reply(so, (void *)0, 0, error); 388 break; 389 390 case WEBDAV_GETATTR: 391 error = filesystem_getattr((struct webdav_request_getattr *)key, 392 (struct webdav_reply_getattr *)&reply); 393 send_reply(so, (void *)&reply, sizeof(struct webdav_reply_getattr), error); 394 break; 395 396 case WEBDAV_READ: 397 bytes = NULL; 398 num_bytes = 0; 399 error = filesystem_read((struct webdav_request_read *)key, 400 &bytes, &num_bytes); 401 send_reply(so, (void *)bytes, (int)num_bytes, error); 402 if (bytes) 403 { 404 free(bytes); 405 } 406 break; 407 408 case WEBDAV_FSYNC: 409 error = filesystem_fsync((struct webdav_request_fsync *)key); 410 send_reply(so, (void *)0, 0, error); 411 break; 412 413 case WEBDAV_REMOVE: 414 error = filesystem_remove((struct webdav_request_remove *)key); 415 send_reply(so, (void *)0, 0, error); 416 break; 417 418 case WEBDAV_RENAME: 419 error = filesystem_rename((struct webdav_request_rename *)key); 420 send_reply(so, (void *)0, 0, error); 421 break; 422 423 case WEBDAV_MKDIR: 424 error = filesystem_mkdir((struct webdav_request_mkdir *)key, 425 (struct webdav_reply_mkdir *)&reply); 426 send_reply(so, (void *)&reply, sizeof(struct webdav_reply_mkdir), error); 427 break; 428 429 case WEBDAV_RMDIR: 430 error = filesystem_rmdir((struct webdav_request_rmdir *)key); 431 send_reply(so, (void *)0, 0, error); 432 break; 433 434 case WEBDAV_READDIR: 435 error = filesystem_readdir((struct webdav_request_readdir *)key); 436 send_reply(so, (void *)0, 0, error); 437 break; 438 439 case WEBDAV_STATFS: 440 error = filesystem_statfs((struct webdav_request_statfs *)key, 441 (struct webdav_reply_statfs *)&reply); 442 send_reply(so, (void *)&reply, sizeof(struct webdav_reply_statfs), error); 443 break; 444 445 case WEBDAV_UNMOUNT: 446 webdav_kill(-2); /* tell the main select loop to exit */ 447 send_reply(so, (void *)0, 0, error); 448 break; 449 450 case WEBDAV_INVALCACHES: 451 error = filesystem_invalidate_caches((struct webdav_request_invalcaches *)key); 452 send_reply(so, (void *)0, 0, error); 453 break; 454 455 case WEBDAV_WRITESEQ: 456 error = filesystem_write_seq((struct webdav_request_writeseq *)key); 457 send_reply(so, (void *)0, 0, error); 458 break; 459 460 case WEBDAV_DUMP_COOKIES: 461 dump_cookies((struct webdav_request_cookies *)key); 462 send_reply(so, (void *)0, 0, error); 463 break; 464 465 case WEBDAV_CLEAR_COOKIES: 466 reset_cookies((struct webdav_request_cookies *)key); 467 send_reply(so, (void *)0, 0, error); 468 break; 469 470 default: 471 error = ENOTSUP; 472 break; 473 } 474 } 475 476#if DEBUG 477 LogMessage(kError, "handle_filesystem_request: error %d, %s(%d)\n", error, 478 (operation==WEBDAV_LOOKUP) ? "LOOKUP" : 479 (operation==WEBDAV_CREATE) ? "CREATE" : 480 (operation==WEBDAV_OPEN) ? "OPEN" : 481 (operation==WEBDAV_CLOSE) ? "CLOSE" : 482 (operation==WEBDAV_GETATTR) ? "GETATTR" : 483 (operation==WEBDAV_SETATTR) ? "SETATTR" : 484 (operation==WEBDAV_READ) ? "READ" : 485 (operation==WEBDAV_WRITE) ? "WRITE" : 486 (operation==WEBDAV_FSYNC) ? "FSYNC" : 487 (operation==WEBDAV_REMOVE) ? "REMOVE" : 488 (operation==WEBDAV_RENAME) ? "RENAME" : 489 (operation==WEBDAV_MKDIR) ? "MKDIR" : 490 (operation==WEBDAV_RMDIR) ? "RMDIR" : 491 (operation==WEBDAV_READDIR) ? "READDIR" : 492 (operation==WEBDAV_STATFS) ? "STATFS" : 493 (operation==WEBDAV_UNMOUNT) ? "UNMOUNT" : 494 (operation==WEBDAV_INVALCACHES) ? "INVALCACHES" : 495 "???", 496 operation 497 ); 498#endif 499 } 500 else { 501 LogMessage(kError, "handle_filesystem_request: get_request failed %d\n", error); 502 send_reply(so, NULL, 0, error); 503 } 504 505 close(so); 506} 507 508/*****************************************************************************/ 509 510static void pulse_thread(void *arg) 511{ 512 #pragma unused(arg) 513 int error; 514 struct node_entry *node; 515 516 error = 0; 517 while ( TRUE ) 518 { 519 struct timespec pulsetime; 520 521 error = pthread_mutex_lock(&pulse_lock); 522 require_noerr(error, pthread_mutex_lock); 523 524 LogMessage(kTrace, "pulse_thread running\n"); 525 526 node = nodecache_get_next_file_cache_node(TRUE); 527 while ( node != NULL ) 528 { 529 if ( NODE_FILE_IS_OPEN(node) ) 530 { 531 /* open node */ 532 if ( !NODE_IS_DELETED(node) ) 533 { 534 /* renew the lock if not deleted */ 535 (void) filesystem_lock(node); 536 } 537 } 538 else 539 { 540 /* remove any closed nodes that are deleted, or that need to be aged out of the list */ 541 if ( NODE_IS_DELETED(node) || NODE_FILE_CACHE_INVALID(node) || purge_cache_files ) 542 { 543 /* it's been closed for WEBDAV_CACHE_TIMEOUT seconds -- remove the node from the file cache */ 544 nodecache_remove_file_cache(node); 545 } 546 } 547 node = nodecache_get_next_file_cache_node(FALSE); 548 } 549 550 /* now, remove any nodes in the deleted list that aren't cached */ 551 nodecache_free_nodes(); 552 553 purge_cache_files = FALSE; /* reset gPurgeCacheFiles (if it was set) */ 554 555 /* sleep for a while */ 556 pulsetime.tv_sec = time(NULL) + (gtimeout_val / 2); 557 pulsetime.tv_nsec = 0; 558 error = pthread_cond_timedwait(&pulse_condvar, &pulse_lock, &pulsetime); 559 require((error == ETIMEDOUT || error == 0), pthread_cond_timedwait); 560 561 /* Ok, unlock so that we can restart the loop */ 562 error = pthread_mutex_unlock(&pulse_lock); 563 require_noerr(error, pthread_mutex_unlock); 564 purge_expired_cookies(); 565 } 566 567pthread_mutex_lock: 568pthread_cond_timedwait: 569pthread_mutex_unlock: 570 571 if ( error ) 572 { 573 webdav_kill(-1); /* tell the main select loop to force unmount */ 574 } 575} 576 577/*****************************************************************************/ 578 579static int handle_request_thread(void *arg) 580{ 581 #pragma unused(arg) 582 int error; 583 webdav_requestqueue_element_t * myrequest; 584 struct timespec timeout; 585 int idleRecheck = 0; 586 587 while (TRUE) { 588 error = pthread_mutex_lock(&requests_lock); 589 require_noerr(error, pthread_mutex_lock); 590 591 /* Check to see if there is a request to process */ 592 593 if (waiting_requests.request_count > 0) { 594 /* There is a request so dequeue it */ 595 idleRecheck = 0; /* reset this flag to indicate that we did find work to do */ 596 myrequest = waiting_requests.item_head; 597 --(waiting_requests.request_count); 598 if (waiting_requests.request_count > 0) { 599 /* There was more than one item on */ 600 /* the queue so bump the pointers */ 601 waiting_requests.item_head = myrequest->next; 602 } 603 else { 604 waiting_requests.item_head = waiting_requests.item_tail = 0; 605 } 606 607 /* Ok, now unlock the queue and go about handling the request */ 608 error = pthread_mutex_unlock(&requests_lock); 609 require_noerr(error, pthread_mutex_unlock); 610 611 switch (myrequest->type) { 612 613 case WEBDAV_REQUEST_TYPE: 614 handle_filesystem_request(myrequest->element.request.socket); 615 break; 616 617 case WEBDAV_DOWNLOAD_TYPE: 618 /* finish the download */ 619 error = network_finish_download(myrequest->element.download.node, myrequest->element.download.readStreamRecPtr); 620 if (error) { 621 /* Set append to indicate that our download failed. It's a hack, but 622 * it should work. Be sure to still mark the download as finished so 623 * that if we were terminated early the closer will be notified 624 */ 625 verify_noerr(fchflags(myrequest->element.download.node->file_fd, UF_APPEND)); 626 myrequest->element.download.node->file_status = WEBDAV_DOWNLOAD_ABORTED; 627 } 628 else { 629 /* Clear flags to indicate that our download is complete. It's a hack, but 630 * it should work. Be sure to still mark the download as finished so 631 * that if we were terminated early the closer will be notified 632 */ 633 verify_noerr(fchflags(myrequest->element.download.node->file_fd, 0)); 634 myrequest->element.download.node->file_status = WEBDAV_DOWNLOAD_FINISHED; 635 } 636 error = 0; 637 break; 638 639 case WEBDAV_SERVER_PING_TYPE: 640 /* Send an OPTIONS request to the server. */ 641 network_server_ping(myrequest->element.serverping.delay); 642 break; 643 644 case WEBDAV_SEQWRITE_MANAGER_TYPE: 645 /* Read the response stream of a sequential write sequence */ 646 network_seqwrite_manager(myrequest->element.seqwrite_read_rsp.ctx); 647 break; 648 649 default: 650 /* nothing we can do, just get the next request */ 651 break; 652 } 653 654 free(myrequest); 655 656 } 657 else { 658 /* There were no requests to handle. If idleRecheck is set, then we just timed out waiting for work 659 and we did one more paranoid check for work and still found no work, so its time to exit the thread. 660 If idleRecheck is not set, then there is no work to do right now. Wait on the condition variable 661 and also have a max timeout to wait. If we timeout and there was no work to do, then set idleRecheck 662 flag and loop around one more time to look for work, just to be paranoid. 663 664 The extra loop around with idleRecheck is for the case where a thread is waiting on condition variable, 665 and it times out. It then gets blocked waiting to acquire the request_lock while a request is being 666 put on the work queue (and thus the idle thread count is greater than 0). It then gets the request_lock 667 and there is not work on the queue for it to do, but the time out has also expired. To be sure that the 668 work gets picked up, we do the extra loop around to check for work. */ 669 if (idleRecheck == 1) { 670 /* still no work to do after timing out and rechecking again for work, so exit thread */ 671 //LogMessage (kSysLog, "handle_request_thread - thread %d exiting since no work to do\n", pthread_self()); 672 gCurrThreadCount -= 1; 673 error = pthread_mutex_unlock(&requests_lock); 674 pthread_exit(NULL); 675 } 676 677 timeout.tv_sec = time(NULL) + WEBDAV_MAX_IDLE_TIME; /* time out in seconds */ 678 timeout.tv_nsec = 0; 679 680 //LogMessage (kSysLog, "handle_request_thread - thread %d idle and waiting for work\n", pthread_self()); 681 gIdleThreadCount += 1; /* increment to indicate one idle thread */ 682 error = pthread_cond_timedwait(&requests_condvar, &requests_lock, &timeout); 683 gIdleThreadCount -= 1; /* decrement number of idle threads */ 684 require((error == ETIMEDOUT || error == 0), pthread_cond_wait); 685 686 if (error == ETIMEDOUT) { 687 /* time out has occurred and still no work to do. Loop around one more time just to be paranoid that 688 there is no work to do */ 689 //LogMessage (kSysLog, "handle_request_thread - thread %d timeout, doing one more check\n", pthread_self()); 690 idleRecheck = 1; 691 } 692 693 /* Unlock so that we can restart the loop */ 694 error = pthread_mutex_unlock(&requests_lock); 695 require_noerr(error, pthread_mutex_unlock); 696 } 697 } 698 699pthread_cond_wait: 700pthread_mutex_unlock: 701pthread_mutex_lock: 702 703 /* errors coming out of this routine are fatal */ 704 if ( error ) { 705 webdav_kill(-1); /* tell the main select loop to force unmount */ 706 } 707 708 return error; 709} 710 711/*****************************************************************************/ 712 713int requestqueue_init() 714{ 715 int error; 716 pthread_mutexattr_t mutexattr; 717 pthread_t the_pulse_thread; 718 pthread_attr_t the_pulse_thread_attr; 719 720 /* set up the lock for connectionstate */ 721 connectionstate = WEBDAV_CONNECTION_UP; 722 723 error = pthread_mutexattr_init(&mutexattr); 724 require_noerr(error, pthread_mutexattr_init); 725 726 error = pthread_mutex_init(&connectionstate_lock, &mutexattr); 727 require_noerr(error, pthread_mutex_init); 728 729 /* initialize requestqueue */ 730 bzero(&waiting_requests, sizeof(waiting_requests)); 731 732 error = pthread_cond_init(&requests_condvar, NULL); 733 require_noerr(error, pthread_cond_init); 734 735 /* set up the lock on the queues */ 736 error = pthread_mutexattr_init(&mutexattr); 737 require_noerr(error, pthread_mutexattr_init); 738 739 error = pthread_mutex_init(&requests_lock, &mutexattr); 740 require_noerr(error, pthread_mutex_init); 741 742 error = pthread_attr_init(&gRequest_thread_attr); 743 require_noerr(error, pthread_attr_init); 744 745 error = pthread_attr_setdetachstate(&gRequest_thread_attr, PTHREAD_CREATE_DETACHED); 746 require_noerr(error, pthread_attr_setdetachstate); 747 748 /* 749 * Start the pulse thread 750 */ 751 purge_cache_files = FALSE; 752 753 error = pthread_mutexattr_init(&mutexattr); 754 require_noerr(error, pthread_mutexattr_init); 755 756 error = pthread_mutex_init(&pulse_lock, &mutexattr); 757 require_noerr(error, pthread_mutex_init); 758 759 error = pthread_cond_init(&pulse_condvar, NULL); 760 require_noerr(error, pthread_cond_init); 761 762 error = pthread_attr_init(&the_pulse_thread_attr); 763 require_noerr(error, pthread_attr_init); 764 765 error = pthread_attr_setdetachstate(&the_pulse_thread_attr, PTHREAD_CREATE_DETACHED); 766 require_noerr(error, pthread_attr_setdetachstate); 767 768 error = pthread_create(&the_pulse_thread, &the_pulse_thread_attr, (void *)pulse_thread, (void *)NULL); 769 require_noerr(error, pthread_create); 770 771pthread_create: 772pthread_attr_setdetachstate: 773pthread_attr_init: 774pthread_cond_init: 775pthread_mutex_init: 776pthread_mutexattr_init: 777 778 return ( error ); 779} 780 781/*****************************************************************************/ 782 783/* requestqueue_enqueue_request 784 * caller exits on errors. 785 */ 786int requestqueue_enqueue_request(int socket) 787{ 788 int error, unlock_error; 789 webdav_requestqueue_element_t * request_element_ptr; 790 pthread_t request_thread; 791 792 error = pthread_mutex_lock(&requests_lock); 793 require_noerr(error, pthread_mutex_lock); 794 795 request_element_ptr = malloc(sizeof(webdav_requestqueue_element_t)); 796 require_action(request_element_ptr != NULL, malloc_request_element_ptr, error = ENOMEM); 797 798 request_element_ptr->type = WEBDAV_REQUEST_TYPE; 799 request_element_ptr->element.request.socket = socket; 800 request_element_ptr->next = 0; 801 ++(waiting_requests.request_count); 802 803 if (!(waiting_requests.item_tail)) { 804 waiting_requests.item_head = waiting_requests.item_tail = request_element_ptr; 805 } 806 else { 807 waiting_requests.item_tail->next = request_element_ptr; 808 waiting_requests.item_tail = request_element_ptr; 809 } 810 811 if (gIdleThreadCount > 0) { 812 /* Already have one or more threads just waiting for work to do. Just kick the requests_condvar to wake 813 up the threads */ 814 error = pthread_cond_signal(&requests_condvar); 815 require_noerr(error, pthread_cond_signal); 816 } 817 else { 818 /* No idle threads, so try to create one if we have not reached out maximum number of threads */ 819 if (gCurrThreadCount < WEBDAV_REQUEST_THREADS) { 820 error = pthread_create(&request_thread, &gRequest_thread_attr, (void *) handle_request_thread, (void *) NULL); 821 require_noerr(error, pthread_create_signal); 822 823 gCurrThreadCount += 1; 824 } 825 } 826 827pthread_create_signal: 828pthread_cond_signal: 829malloc_request_element_ptr: 830 831 unlock_error = pthread_mutex_unlock(&requests_lock); 832 require_noerr_action(unlock_error, pthread_mutex_unlock, error = (error == 0) ? unlock_error : error); 833 834pthread_mutex_unlock: 835pthread_mutex_lock: 836 837 return (error); 838} 839 840/*****************************************************************************/ 841 842int requestqueue_enqueue_download(struct node_entry *node, struct ReadStreamRec *readStreamRecPtr) 843{ 844 int error, error2; 845 webdav_requestqueue_element_t * request_element_ptr; 846 pthread_t request_thread; 847 848 error = pthread_mutex_lock(&requests_lock); 849 require_noerr_action(error, pthread_mutex_lock, webdav_kill(-1)); 850 851 request_element_ptr = malloc(sizeof(webdav_requestqueue_element_t)); 852 require_action(request_element_ptr != NULL, malloc_request_element_ptr, error = EIO); 853 854 request_element_ptr->type = WEBDAV_DOWNLOAD_TYPE; 855 request_element_ptr->element.download.node = node; 856 request_element_ptr->element.download.readStreamRecPtr = readStreamRecPtr; 857 858 /* Insert downloads at head of request queue. They must be executed immediately since the download is holding a stream reference. */ 859 request_element_ptr->next = waiting_requests.item_head; 860 ++(waiting_requests.request_count); 861 862 if ( waiting_requests.item_head == NULL ) { 863 /* request queue was empty */ 864 waiting_requests.item_head = waiting_requests.item_tail = request_element_ptr; 865 } 866 else { 867 /* this request is the new head */ 868 waiting_requests.item_head = request_element_ptr; 869 } 870 871 if (gIdleThreadCount > 0) { 872 /* Already have one or more threads just waiting for work to do. Just kick the requests_condvar to wake 873 up the threads */ 874 error = pthread_cond_signal(&requests_condvar); 875 require_noerr(error, pthread_cond_signal); 876 } 877 else { 878 /* No idle threads, so try to create one if we have not reached out maximum number of threads */ 879 if (gCurrThreadCount < WEBDAV_REQUEST_THREADS) { 880 error = pthread_create(&request_thread, &gRequest_thread_attr, (void *) handle_request_thread, (void *) NULL); 881 require_noerr(error, pthread_create_signal); 882 883 gCurrThreadCount += 1; 884 } 885 } 886 887pthread_create_signal: 888pthread_cond_signal: 889malloc_request_element_ptr: 890 891 error2 = pthread_mutex_unlock(&requests_lock); 892 require_noerr_action(error2, pthread_mutex_unlock, error = (error == 0) ? error2 : error; webdav_kill(-1)); 893 894pthread_mutex_unlock: 895pthread_mutex_lock: 896 897 return (error); 898} 899 900/*****************************************************************************/ 901 902int requestqueue_enqueue_server_ping(u_int32_t delay) 903{ 904 int error, error2; 905 webdav_requestqueue_element_t * request_element_ptr; 906 pthread_t request_thread; 907 908 error = pthread_mutex_lock(&requests_lock); 909 require_noerr_action(error, pthread_mutex_lock, webdav_kill(-1)); 910 911 request_element_ptr = malloc(sizeof(webdav_requestqueue_element_t)); 912 require_action(request_element_ptr != NULL, malloc_request_element_ptr, error = EIO); 913 914 request_element_ptr->type = WEBDAV_SERVER_PING_TYPE; 915 request_element_ptr->element.serverping.delay = delay; 916 917 /* Insert server pings at head of request queue. They must be executed immediately since they are */ 918 /* used to detect when connectivity to the host has been restored. */ 919 request_element_ptr->next = waiting_requests.item_head; 920 ++(waiting_requests.request_count); 921 922 if ( waiting_requests.item_head == NULL ) { 923 /* request queue was empty */ 924 waiting_requests.item_head = waiting_requests.item_tail = request_element_ptr; 925 } 926 else { 927 /* this request is the new head */ 928 waiting_requests.item_head = request_element_ptr; 929 } 930 931 if (gIdleThreadCount > 0) { 932 /* Already have one or more threads just waiting for work to do. Just kick the requests_condvar to wake 933 up the threads */ 934 error = pthread_cond_signal(&requests_condvar); 935 require_noerr(error, pthread_cond_signal); 936 } 937 else { 938 /* No idle threads, so try to create one if we have not reached out maximum number of threads */ 939 if (gCurrThreadCount < WEBDAV_REQUEST_THREADS) { 940 error = pthread_create(&request_thread, &gRequest_thread_attr, (void *) handle_request_thread, (void *) NULL); 941 require_noerr(error, pthread_create_signal); 942 943 gCurrThreadCount += 1; 944 } 945 } 946 947pthread_create_signal: 948pthread_cond_signal: 949malloc_request_element_ptr: 950 951 error2 = pthread_mutex_unlock(&requests_lock); 952 require_noerr_action(error2, pthread_mutex_unlock, error = (error == 0) ? error2 : error; webdav_kill(-1)); 953 954pthread_mutex_unlock: 955pthread_mutex_lock: 956 957 return (error); 958} 959 960/*****************************************************************************/ 961 962int requestqueue_enqueue_seqwrite_manager(struct stream_put_ctx *ctx) 963{ 964 int error, error2; 965 webdav_requestqueue_element_t * request_element_ptr; 966 pthread_t request_thread; 967 968 error = pthread_mutex_lock(&requests_lock); 969 require_noerr_action(error, pthread_mutex_lock, webdav_kill(-1)); 970 971 request_element_ptr = malloc(sizeof(webdav_requestqueue_element_t)); 972 require_action(request_element_ptr != NULL, malloc_request_element_ptr, error = EIO); 973 974 request_element_ptr->type = WEBDAV_SEQWRITE_MANAGER_TYPE; 975 request_element_ptr->element.seqwrite_read_rsp.ctx = ctx; 976 977 request_element_ptr->next = waiting_requests.item_head; 978 ++(waiting_requests.request_count); 979 980 if ( waiting_requests.item_head == NULL ) { 981 /* request queue was empty */ 982 waiting_requests.item_head = waiting_requests.item_tail = request_element_ptr; 983 } 984 else { 985 /* this request is the new head */ 986 waiting_requests.item_head = request_element_ptr; 987 } 988 989 if (gIdleThreadCount > 0) { 990 /* Already have one or more threads just waiting for work to do. Just kick the requests_condvar to wake 991 up the threads */ 992 error = pthread_cond_signal(&requests_condvar); 993 require_noerr(error, pthread_cond_signal); 994 } 995 else { 996 /* No idle threads, so try to create one if we have not reached out maximum number of threads */ 997 if (gCurrThreadCount < WEBDAV_REQUEST_THREADS) { 998 error = pthread_create(&request_thread, &gRequest_thread_attr, (void *) handle_request_thread, (void *) NULL); 999 require_noerr(error, pthread_create_signal); 1000 1001 gCurrThreadCount += 1; 1002 } 1003 } 1004 1005pthread_create_signal: 1006pthread_cond_signal: 1007malloc_request_element_ptr: 1008 1009 error2 = pthread_mutex_unlock(&requests_lock); 1010 require_noerr_action(error2, pthread_mutex_unlock, error = (error == 0) ? error2 : error; webdav_kill(-1)); 1011 1012pthread_mutex_unlock: 1013pthread_mutex_lock: 1014 1015 return (error); 1016} 1017 1018/*****************************************************************************/ 1019 1020int requestqueue_purge_cache_files(void) 1021{ 1022 int error; 1023 1024 error = pthread_mutex_lock(&pulse_lock); 1025 require_noerr(error, pthread_mutex_lock); 1026 1027 /* set up for a purge */ 1028 purge_cache_files = TRUE; 1029 1030 /* wake up pulse_thread to do the work */ 1031 error = pthread_cond_signal(&pulse_condvar); 1032 require_noerr(error, pthread_cond_signal); 1033 1034pthread_cond_signal: 1035 1036 error = pthread_mutex_unlock(&pulse_lock); 1037 1038pthread_mutex_lock: 1039 1040 return ( error ); 1041} 1042 1043/*****************************************************************************/ 1044