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