1 /* ========================================================================== **
5 * Copyright (C) 2012 by Christopher R. Hertel
7 * Email: crh@ubiqx.mn.org
9 * $Id: PrequelD.c 2012-09-08 23:20:22 -0500 crh$
11 * -------------------------------------------------------------------------- **
14 * PeerDist protocol server daemon.
16 * -------------------------------------------------------------------------- **
20 * This program is free software: you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation, either version 3 of the License, or
23 * (at your option) any later version.
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
30 * You should have received a copy of the GNU General Public License
31 * along with this program. If not, see <http://www.gnu.org/licenses/>.
33 * -------------------------------------------------------------------------- **
34 * This code was developed in participation with
35 * the Protocol Freedom Information Foundation.
36 * See http://www.protocolfreedom.org/ for more information.
37 * -------------------------------------------------------------------------- **
41 * The Prequel Project website is http://www.ubiqx.org/proj/Prequel/
45 * PeerDist - The protocol and/or encoding format used by Microsoft's
46 * BranchCacheTM distributed caching system.
48 * Prequel - The name of an Open Source project aimed at implementing
49 * PeerDist for use with Linux, *BSD, and other Unix-like
53 * [BCOVIEW]: MS W2K8R2 BranchCache Overview
54 * http://technet.microsoft.com/en-us/library/dd637832.aspx
56 * [MS-CCRSOD]: Content Caching and Retrieval System Overview
57 * http://msdn.microsoft.com/en-us/library/ff632262.aspx
59 * [MS-PCCRC]: Peer Content Caching and Retrieval: Content Identification
60 * http://msdn.microsoft.com/en-us/library/dd303704.aspx
63 * cc -I.. -o PrequelD PrequelD.c Gstr.c PD_peerdist1.c PD_read_config.c \
64 * PD_sha2_oSSL.c PD_utils.c ../ubi_sLinkList.c -lcrypto -lpthread
67 * + Add support for Unicode pathnames.
69 * + The background thread should traverse source directories and generate
70 * hash caches for any file that meets criteria and does not have an
71 * up-to-date hash cache file.
73 * + The background thread should traverse cache directories and delete
74 * any cache files that are out-of-date or which map back to a source
75 * file that no longer exists (or has been truncated to a size that is
76 * smaller than the minimum).
78 * + Add PeerDist v2 support.
80 * + Add signal handling:
81 * - HUP: Lock the mutex and re-read the config file. Update directory
83 * - TERM: Exit cleanly.
85 * + Long-term: Collect interesting statistics and log them on SIGHUP,
86 * SIGTERM, or SIGUSR1.
88 * + Add a -s <socket> option to override the socket path in the config
92 * + If running as root, chown the target file to match the source file
93 * ownership (and ACLs?).
95 * + We should not be generating new hash files if the existing hash files
98 * + Need to detect whether we are running already or not. We can run
99 * multiple daemons at once, as long as their src directories don't
100 * overlap and they are using different sockets.
102 * ========================================================================== **
105 #include <time.h> /* For nanosleep(2). */
106 #include <stdlib.h> /* Standard Library. */
107 #include <stdbool.h> /* Standard boolean type. */
108 #include <stdint.h> /* Standard extended integer types. */
109 #include <unistd.h> /* For getopt(3), close(2), unlink(2), etc. */
110 #include <string.h> /* For strerror(3), strchr(3). */
111 #include <stdarg.h> /* Standard variable argument lists. */
112 #include <ctype.h> /* For isspace(3). */
113 #include <sys/types.h> /* For the pid_t and DIR* types. */
114 #include <dirent.h> /* For reading directory entries. */
115 #include <pthread.h> /* For POSIX Threads. */
116 #include <poll.h> /* For ppoll(2) and struct pollfd. */
117 #include <sys/socket.h> /* Socket(7) interface header. */
118 #include <sys/un.h> /* Unix(7) domain sockets. */
120 #include "PD_peerdist1.h" /* PeerDist v1 protocol support. */
121 #include "PD_read_config.h" /* Read the configuration file. */
122 #include "PD_utils.h" /* General PrequelD utilities. */
123 #include "ubi_sLinkList.h" /* Linked List module. */
124 #include "Gstr.h" /* Growable string buffers. */
127 /* -------------------------------------------------------------------------- **
130 * DEFAULT_CONFIG_FILE - Default pathname of the configuration file to read
131 * on startup. This value can be overridden at
134 * SYSLOG_NAME - The name used to identify this program in Syslog
137 * RECV_TIMEOUT - Number of seconds and microseconds to listen on
138 * an input socket while receiving data. Format as
139 * an initialization pair: { <sec>, <usec> }.
140 * 1usec = 1/1000000sec (10^-6, or 1/1000msec)
142 * POLL_TIMEOUT - Number of milliseconds to wait when poll(2)ing
143 * an input socket. 1msec = 1/1000sec == 1000usec.
145 * bSIZE - A buffer size used to allocate the read buffer
146 * included in the <RecvBufr> structure.
149 #ifndef DEFAULT_CONFIG_FILE
150 #define DEFAULT_CONFIG_FILE "/etc/prequeld/pd.conf"
153 #define SYSLOG_NAME "prequeld"
155 #define RECV_TIMEOUT { 1, 0 } /* 1 second. */
156 #define POLL_TIMEOUT 30000 /* 30 seconds. */
161 /* -------------------------------------------------------------------------- **
164 * forEach( V, L ) - A macro for traversing a linked list of <pd_ConfigRec>
166 * V - The variable that will point to each node in the
167 * list. V must be substituted by a pointer to a
169 * L - The list to be traversed.
173 #define forEach( V, L ) \
174 for( (V) = (pd_ConfigRec *)ubi_slFirst( (L) ); \
176 (V) = (pd_ConfigRec *)ubi_slNext( (V) ) )
179 /* -------------------------------------------------------------------------- **
182 * RangeVal - Used to read and interpret the value of the Range: option,
183 * which may be sent by the client. If <given> is false, the
184 * we know that the client did not provide a value. If <given>
185 * is true, then the client did provide a value and that value
186 * is stored in the <val> field.
188 * ProtVers - PeerDist protocol major/minor number structure.
190 * WorkItem - A queue entry and a string pointer pointing to the pathname
191 * of a source file that is in need of hashing.
193 * RecvBufr - Used to store data read from a socket (or other). This allows
194 * us to read in chunks but consume the data piecemeal. Think of
195 * it as sort of a minimalist socket equivalent of a FILE type.
197 * RecvErrs - A set of errors that may be returned from the little socket
198 * reading subsystem we've got going here.
203 bool given; /* Was a value given. */
204 uint64_t val; /* The given value. */
209 unsigned char major; /* PeerDist major version number. */
210 unsigned char minor; /* PeerDist minor version number. */
215 ubi_slNode node; /* Linked list node. */
216 char *srcPath; /* Source file path. */
221 int sock; /* The I/O socket in use. */
222 size_t pos; /* Current byte offset. */
223 size_t len; /* Bytes stored in the buffer. */
224 unsigned char bufr[bSIZE]; /* Data storage buffer. */
229 rv_success = 0, /* Not an error. All okay. */
230 rv_end = -1, /* Received a blank line. */
231 rv_closed = -2, /* Connection closed by peer (EOF). */
232 rv_timeout = -3, /* Timeout waiting for input. */
233 rv_invalid = -4, /* Invalid input via pipe socket. */
234 rv_nomemory = -5, /* Memory allocation failure. */
235 rv_error = -6 /* recv(2) error. Check <errno>. */
239 /* -------------------------------------------------------------------------- **
242 * Copyright - Copyright information.
243 * License - License information string.
244 * Revision - Revision string, generated by revision control.
245 * Id - Longer revision string.
247 * HelpMsg - The program help message.
248 * VerboseMsg - Verbose help message.
251 static const char *Copyright =
252 "Copyright (c) 2012 by Christopher R. Hertel";
253 static const char *License =
254 "License: GNU General Public License version 3 (GPLv3)\n"
255 " http://www.gnu.org/licenses/gpl-3.0.html";
256 static const char *Revision =
258 static const char *Id =
259 "$Id: PrequelD.c 2012-09-08 23:20:22 -0500 crh$";
261 static const char *HelpMsg[] =
263 " Available Options:",
264 " -c <file> Specify the configuration file to use.",
265 " -f Run in the foreground, not as a daemon.",
266 " -h Produce this useful help message, then exit (-hv == more help).",
267 " -l <file> Send log messages to <file>.",
268 " -q Be quiet. Set the verbosity level to zero.",
269 " -t Test-parse the configuration file, then exit.",
270 " -v Be verbose. Add more -v's for more verbosity.",
271 " -V Output version information.",
275 static const char *VerboseMsg[] =
277 "The Options Available to You:",
278 " -c <file> Specify the configuration file to use.",
279 " Default: \"" DEFAULT_CONFIG_FILE "\".",
280 " -f Run in the foreground, not as a daemon.",
281 " Default: Run as a daemon.",
282 " -h Produce a useful help message, then exit.",
283 " Default: Don't produce a useful help message. Run normally.",
284 " -l <file> Send log messages to <file>.",
285 " Default: When running in the foreground, log messages are sent",
286 " to <stderr>. When running in the background, log",
287 " messages are sent to syslog. All initial messages",
288 " (as the program starts up) are sent to <stdout> or",
290 " -q Be quiet. Set the verbosity level to zero (0) and produce only",
291 " the minimum set of diagnostic messages. Default verbosity: 1.",
292 " -t Parse the configuration file and report any errors to <stderr>.",
293 " The program will exit once parsing is complete.",
294 " Use -tv to dump the contents of a successfully parsed",
295 " configuration file.",
296 " -v Be verbose. Add more -v's for more verbosity.",
297 " Use of -q or -v overrides the config file verbosity settings.",
298 " Default verbosity: 1.",
299 " -V Output version information.",
300 " Increased verbosity provides copyright and license information.",
305 /* -------------------------------------------------------------------------- **
308 * Verbosity - Controls the level of diagnostic messages that are
309 * produced. The minimum value is 0. Anything above 10
310 * is ridiculous. Default: 1. A maximum value of 255
311 * is enforced both in the PD_read_config module and in
312 * <ReadOpts()> function, below.
314 * Daemonize - By default PrequelD will run as a daemon, but this
315 * default behavior can be overridden with a command-line
316 * option. There is no configuration file option for this,
317 * so we use a global variable to represent this attribute.
319 * Cfg - Pointer used to retrieve the configuration information
320 * from the configuration file.
322 * WorkerThread - The thread ID of the background thread that is doing all
325 * GlobalMutex - A global mutex attribute. The worker thread needs brief
326 * access to the <Cfg> list and other state attribues.
327 * This allows the foreground and background to share.
329 * Shutdown - Used to let the worker process know when it is time to
330 * shut down and return.
332 * WorkQueue - A queue of source file names that are waiting to be
333 * hashed. The queue is filled when a request is made
334 * and no hash cache file is found. It is emptied by
335 * the worker thread. Queued entries have priority over
336 * source files found during directory traversal.
339 static unsigned int Verbosity = 1;
340 static bool Daemonize = true;
341 static pd_Config *Cfg = NULL;
342 static pthread_t WorkerThread;
343 static pthread_mutex_t GlobalMutex = PTHREAD_MUTEX_INITIALIZER;
344 static bool Shutdown = false;
345 static ubi_slList WorkQueue[1];
348 /* -------------------------------------------------------------------------- **
352 static void Usage( const char *progname, int status )
353 /* ------------------------------------------------------------------------ **
354 * Provide usage information, then exit.
356 * Input: progname - Pointer to a string containing the program name.
357 * status - Exit status to return.
361 * Notes: Output is sent to <stdout> so that it can be piped through
362 * 'more' or 'less' or something else.
364 * ------------------------------------------------------------------------ **
368 const char **msg = (Verbosity > 1) ? VerboseMsg : HelpMsg;
370 Say( "Usage: %s [options]\n", progname );
371 for( i = 0; NULL != msg[i]; i++ )
372 Say( "%s\n", msg[i] );
378 static void LockLog( char *fmt, ... )
379 /* ------------------------------------------------------------------------ **
380 * Lock/unlock the global mutex to safely write a log message.
382 * Input: fmt - Format string, as used in printf(), etc.
383 * ... - Variable parameter list.
387 * Notes: A simple wrapper around <vLog()> in PD_utils.
389 * See: <PD_utils.vLog()>
391 * ------------------------------------------------------------------------ **
396 if( 0 == pthread_mutex_lock( &GlobalMutex ) )
401 (void)pthread_mutex_unlock( &GlobalMutex );
406 static bool SendResponse( int sock, char *fmt, ... )
407 /* ------------------------------------------------------------------------ **
408 * Send a response to the client.
410 * Input: sock - The socket on which to write the response message.
411 * fmt - A format string to be written to the socket. It should
412 * contain all required newlines.
413 * ... - Parameters used to fill in the response string.
415 * Output: True if the message was composed and sent, false otherwise.
417 * Notes: We only check that we have written *something*, not that all
418 * of the message was sent. We assume that all will be sent if
421 * ------------------------------------------------------------------------ **
428 if( NULL == initGstr( gs ) )
431 gs->len = 1 + vsnprintf( gs->bufr, gs->bSize, fmt, ap );
434 /* If we didn't have enough room, expand the string and try again. */
435 if( (gs->len) > (gs->bSize) )
437 if( growGstr( gs, gs->len ) < 0 )
439 (void)freeGstr( gs );
443 gs->len = 1 + vsprintf( gs->bufr, fmt, ap );
447 /* Now write it, but don't send the trailing NUL byte. */
448 result = write( sock, strGstr( gs ), (gs->len - 1) );
456 static int RecvGetc( RecvBufr *Rb )
457 /* ------------------------------------------------------------------------ **
458 * Read a single character from the socket... sort of like getc(3).
460 * Input: Rb - A pointer to a RecvBufr structure.
462 * Output: An integer. A non-negative value is the byte read, and should
463 * be cast to (unsigned char). A negative value is an error code.
465 * Notes: The <RecvErrs> type defines the list of possible error codes.
466 * Note, however, that a return value of zero (0) should not be
467 * interpreted as <rv_success>. It should be interpreted as '\0'
468 * a valid input character.
470 * The buffer is refilled if it is empty, which means that the
471 * most recent character returned will still be in the buffer.
472 * so "unget" of one character is always safe. There are no
473 * guarantees beyond that.
475 * ------------------------------------------------------------------------ **
480 if( Rb->pos < Rb->len )
482 /* The buffer still has bytes. */
483 return( (int)(Rb->bufr[(Rb->pos++)]) );
486 /* We need to fill our buffer. */
487 result = recv( Rb->sock, Rb->bufr, bSIZE, 0 );
489 if( result > 0 ) /* Success */
493 return( (int)Rb->bufr[0] );
496 if( result < 0 ) /* Error */
500 if( (EAGAIN == errno) || (EWOULDBLOCK == errno) )
501 return( rv_timeout );
505 /* Client close the connection (result == 0). */
510 static RecvErrs RecvGetLine( RecvBufr *Rb, Gstr *gsBufr )
511 /* ------------------------------------------------------------------------ **
512 * Read a newline-terminated line from the socket.
514 * Input: Rb - A pointer to a RecvBufr structure.
515 * gsBufr - A growable string buffer, used as a place to store
516 * the line we are reading.
518 * Output: One of the codes defined by the RecvErrs enumerated type.
520 * Notes: The terminating newline ('\n') is not copied into the <gsBufr>
523 * An error code does not mean that the line has not been read,
524 * just that a connection status change was discovered while
525 * reading the line. That could be a timeout, a system error,
526 * or a closed connection. The caller may be able to use the
527 * input that was received, if any. Check the size of <gsBufr>
528 * to see if there is any content present. If a memory
529 * allocation error occurs... all bets are off.
531 * Lines are delimited by a newline ('\n') only. Control
532 * characters other than '\n' are converted to spaces, and runs
533 * of whitespace are reduced to a single space.
535 * We use growable strings because the lines are typically fairly
536 * small, but a long pathname could change that.
538 * ------------------------------------------------------------------------ **
543 clearGstr( gsBufr ); /* Empty the buffer. */
545 while( (c = RecvGetc( Rb )) >= 0 )
547 if( '\n' == c ) /* Eoln terminates the line. */
548 return( rv_success );
550 /* Reduce whitespace runs to a single space. */
551 if( isspace( c ) || iscntrl( c ) )
556 if( c < 0 ) /* Check for and return error codes. */
557 return( (RecvErrs)c );
558 if( '\n' == c ) /* Eoln terminates the line. */
559 return( rv_success );
560 } while( isspace( c ) || iscntrl( c ) );
561 if( addcGstr( gsBufr, ' ' ) < 0 )
562 return( rv_nomemory );
565 /* Whatever character we have now, it's not whitespace and it's not
566 * a control character. Add it to the string.
568 if( addcGstr( gsBufr, (char)c ) < 0 ) /* Add the character to the word... */
569 return( rv_nomemory ); /* ...and hope this never happens. */
572 /* This'll be a RecvErrs error code; a negative value. */
573 return( (RecvErrs)c );
577 static RecvErrs ParseRequest( RecvBufr *Rb )
578 /* ------------------------------------------------------------------------ **
579 * Parse and respond to a client QUEUE request.
581 * Input: Rb - A pointer to a receive buffer.
583 * Output: A <RecvErrs> error code.
585 * Notes: This is where it happens. Parse a QUEUE request and, if valid,
586 * queue the requested file for hashing.
588 * ------------------------------------------------------------------------ **
591 const char *s; /* General purpose string pointer. */
592 Gstr gsBufr[1]; /* A growable string to receive input. */
593 RecvErrs result; /* Function call results. */
594 WorkItem *NewEntry; /* New entry into the <WorkQueue>. */
596 /* Initialize the Gstr. */
597 if( NULL == initGstr( gsBufr ) )
598 return( rv_nomemory );
600 /* Try to read a command. */
601 result = RecvGetLine( Rb, gsBufr );
602 if( rv_success != result )
604 /* Report errors reading the line. */
608 if( isemptyGstr( gsBufr ) )
610 /* Read succeeded, but the line is empty. */
615 /* We've got a string. See if it's a valid QUEUE request. */
616 s = strGstr( gsBufr );
617 if( 0 == strncasecmp( "QUEUE ", s, 6 ) )
621 /* Invalid request line. */
623 return( rv_invalid );
626 /* The rest of the input line is the source file name. Queue it.
627 * Don't worry about bad paths. They'll be caught by the worker thread.
629 NewEntry = (WorkItem *)malloc( (sizeof( WorkItem ) + strlen( s ) + 1) );
630 if( NULL == NewEntry )
633 return( rv_nomemory );
635 /* Copy the source pathname into the queue entry, then add it to the queue. */
636 NewEntry->srcPath = strcpy( (char *)(&(NewEntry[1])), s );
637 if( 0 == pthread_mutex_lock( &GlobalMutex ) )
639 (void)ubi_slEnqueue( WorkQueue, NewEntry );
641 Log( "Enqueued: %s\n", NewEntry->srcPath );
642 (void)pthread_mutex_unlock( &GlobalMutex );
651 static void *Responder( void *thingy )
652 /* ------------------------------------------------------------------------ **
653 * Respond to a request from a client, as a subthread.
655 * Input: thingy - A pointer to an integer, which is the open
656 * communications socket.
660 * Notes: This is, essentially, the mainline of a thread that is
661 * spawned to deal with a single client connection.
663 * The client may send one or more lines (terminated with a
664 * newline ('\n'). Each line may be either a QUERY line, or a
665 * blank line. A blank line terminates the conversation.
667 * For each QUERY line, we attempt to add the given pathname to
668 * to the priority queue. If that succeeds, we return "OKAY\n".
669 * If it fails, we return "ERROR: %s\n", where "%s" is replaced
670 * with an error message.
672 * ------------------------------------------------------------------------ **
675 int client_sock = *((int *)thingy); /* Copy the socked id. */
676 bool run = true; /* Ready to run. */
677 struct timeval tv[1] = { RECV_TIMEOUT };
679 struct pollfd pfd[1];
682 /* Free unused memory and detach the thread so we can just exit when done. */
684 (void)pthread_detach( pthread_self() );
686 /* Log entry to let the world know we're running. */
688 LockLog( "Responder() running to handle a new connection.\n" );
690 /* Prepare the pollfd structure, set a timeout on the i/o socket. */
691 pfd->fd = client_sock;
692 pfd->events = POLLIN;
694 if( setsockopt( client_sock, SOL_SOCKET, SO_RCVTIMEO,
695 (void *)tv, sizeof( struct timeval ) ) )
697 /* This should never happen. */
698 LockLog( "Error setting timeout on socket; %s.\n", ErrStr );
702 /* Prepare the receive buffer. */
703 (void)memset( Rb, 0, sizeof( RecvBufr ) );
704 Rb->sock = client_sock;
706 /* Read and handle request message lines, one at a time. */
709 /* Try to parse a single line of input, then handle the result. */
710 switch( ParseRequest( Rb ) )
712 case rv_success: /* Success is good. Keep going. */
713 SendResponse( Rb->sock, "OKAY\n" );
714 case rv_timeout: /* No input? We'll try again. */
716 case rv_nomemory: /* Lack of memory, bail out. */
717 LockLog( "Error: Memory allocation failure in subthread.\n" );
720 case rv_error: /* System error. */
721 LockLog( "Error: Socket error in subthread; %s.\n", ErrStr );
722 SendResponse( Rb->sock, "ERROR: Server encountered a system error.\n" );
725 default: /* Blank line or closed connection. */
729 /* If we get this far...
730 * Wait to see if there is new data on its way.
731 * If not, close the connection.
735 result = poll( pfd, 1, POLL_TIMEOUT );
738 LockLog( "Warning: Timeout waiting for input on a socket.\n" );
743 LockLog( "Error: Waiting for input on a socket; %s.\n", ErrStr );
749 /* All done. Close the socket and the thread. */
750 (void)close( client_sock );
755 static void Listener( const int lsock )
756 /* ------------------------------------------------------------------------ **
757 * Listen for incomming connection requests and spawn responder threads.
759 * Input: lsock - Listening socket.
763 * Notes: This function is run within the primary thread, but it is
764 * started after the worker thread has been spun off so it
765 * must lock the global mutex before accessing any commonly
768 * We allocate an integer from the heap, copy the socket number,
769 * and pass the pointer to the child. This is done in part to
770 * avoid an annoying problem. On 64-bit systems (it seems) the
771 * integer type is a different size than the void * type. Doing
772 * things the old fashioned way (casting the int to void * and
773 * then casting back within the child) generates a legitimate
774 * warning. Casting between different size types may not work
775 * as we would expect.
777 * As long as we can get the integer from memory, this method is
778 * more 'correct'... sort of.
780 * See Also: <Responder()>
782 * ------------------------------------------------------------------------ **
788 pthread_t threadhandle;
790 /* Wait for connection requests, then spawn off threads to handle the
791 * incomming connections.
793 while( (newSock = accept( lsock, NULL, NULL )) >= 0 )
795 parptr = (int *)malloc( sizeof( int ) );
799 result = pthread_create( &threadhandle, NULL, &Responder, parptr );
801 if( (NULL == parptr) || result ) /* Check for problems. */
803 if( 0 == pthread_mutex_lock( &GlobalMutex ) )
808 "Failure: Memory allocation failure creating subthread.\n" );
812 /* We free <parptr> here because the subthread was not created.
813 * Normally, <parptr> would be freed by the subthread.
815 Log( "Warning: Error creating responder subthread; %s.\n",
816 strerror( result ) );
819 (void)pthread_mutex_unlock( &GlobalMutex );
821 (void)close( newSock );
825 /* Lock the mutex to write the error message to the log.
826 * To get here, there must have been an error returned by <accept()>.
828 if( 0 == pthread_mutex_lock( &GlobalMutex ) )
830 /* The pthread lock does not modify errno, so we don't need to store it. */
831 Log( "Failure: Error accepting socket connection; %s.\n", ErrStr );
832 (void)pthread_mutex_unlock( &GlobalMutex );
838 static void WorkHash( const char *srcPathName )
839 /* ------------------------------------------------------------------------ **
840 * Create a hash cache file given a source file name.
842 * Input: srcPathName - The pathname of the source file to be hashed.
846 * Notes: Dancing with mutex.
848 * ------------------------------------------------------------------------ **
854 pd_v1HashType hashType;
855 unsigned char key[PD_V1_KEY_SIZE];
859 /* Try to grab the mutex.
860 * If we can't grab the mutex, then something's wrong but we cannot log it
861 * because we need the mutex in order to (safely) write to the log. We'll
864 if( (result = pthread_mutex_lock( &GlobalMutex )) )
868 * Find the config record that points to the source directory tree in
869 * which the source file resides.
871 entry = pd_FindSrcDir( Cfg, srcPathName );
875 Log( "Requested file is not in scope: %s\n", srcPathName );
876 (void)pthread_mutex_unlock( &GlobalMutex );
880 /* We have mutex and a configuration entry.
881 * Copy all of the config values so that we can release the mutex.
883 if( entry->cacheDir )
885 cacheDir = strdup( entry->cacheDir );
886 minBlocks = entry->minBlocks;
887 hashType = entry->v1HashType;
889 (void)memcpy( key, entry->userParam, PD_V1_KEY_SIZE );
893 /* This should be impossible.
894 * Check PD_read_config if this ever happens.
896 Log( "Bad configuration entry; cacheDir is NULL.\n" );
897 (void)pthread_mutex_unlock( &GlobalMutex );
901 /* Check the <cacheDir> copy. */
902 if( NULL == cacheDir )
905 Log( "Warning: Memory allocation failure in WorkHash().\n" );
909 /* Release the mutex and calculate the hashes.
910 * This step may take a while, which is why we don't want to be holding
911 * the mutex just now.
913 (void)pthread_mutex_unlock( &GlobalMutex );
914 result = pd_v1CreateHashCache( srcPathName,
923 /* Report the results.
924 * It is not a fatal error to fail to create the cache file, but it should
925 * probably be logged. Logging requires the mutex, so we have to grab it
930 if( pthread_mutex_lock( &GlobalMutex ) )
931 return; /* Error getting the mutex... can't Log() without it. */
933 if( pd_Success == result )
936 Log( "Hashed: %s\n", srcPathName );
941 Log( "Failure hashing \"%s\"; %s; %s.\n", srcPathName,
942 pd_v1StrResult( result ), strerror( syserr ) );
944 Log( "Failure hashing \"%s\"; %s.\n", srcPathName,
945 pd_v1StrResult( result ) );
948 /* And release the mutex again. */
949 (void)pthread_mutex_unlock( &GlobalMutex );
954 static void *WorkerMain( void *thingy )
955 /* ------------------------------------------------------------------------ **
956 * Worker thread mainline.
958 * Input: thingy - Pointer to an opaque blob that currently isn't used.
960 * Output: Always returns NULL.
962 * Notes: This code runs as a background thread. It's job is to generate
963 * the hash cache files.
965 * FIX: The wDir state is designed to allow the worker thread to
966 * traverse configured source directories and hash files that
967 * have not been requested yet. The requested files, listed
968 * in the <WorkQueue> have higher priority. Unfortunately,
969 * directory traversal has not been implemented yet.
971 * In addition, we should traverse the cache directories and
972 * purge cache files that are out of date or for which the
973 * source file no longer exists.
975 * ------------------------------------------------------------------------ **
978 static enum { wNone, wStop, wQueue, wDir } doState; /* What shall we do? */
979 static struct timespec ts = { 0, 500000000 }; /* Half a second. */
980 WorkItem *srcEntry = NULL;
984 /* Start our loop by waiting a half second, just to pace ourselves. */
985 (void)nanosleep( &ts, NULL );
987 /* Determine which action to take, and collect any required data.
988 * This requires locking/unlocking the global mutex.
990 doState = wNone; /* Default action. */
991 if( 0 == pthread_mutex_lock( &GlobalMutex ) )
997 if( 0 == ubi_slCount( WorkQueue ) )
1001 srcEntry = (WorkItem *)ubi_slDequeue( WorkQueue );
1002 if( (NULL != srcEntry) && (NULL != srcEntry->srcPath) )
1006 Log( "Dequeued: %s\n", srcEntry->srcPath );
1009 Log( "Error: NULL entry in priority queue.\n" );
1012 (void)pthread_mutex_unlock( &GlobalMutex );
1015 /* Now do the thing. */
1019 /* Hash an entry from the queue, then free the entry as a whole. */
1022 WorkHash( srcEntry->srcPath );
1027 /* Hash an entry from a source directory. */
1028 /* FIX: Need directory traversal. */
1034 } while( wStop != doState );
1040 static void StartWorker( void )
1041 /* ------------------------------------------------------------------------ **
1042 * Start a background thread to generate hash cache files.
1047 * Notes: The worker thread does the work of creating hash cache files
1048 * from source content. It runs separately so that it does not
1049 * interfere with communication with our clients.
1051 * ------------------------------------------------------------------------ **
1055 pthread_attr_t attr[1];
1057 /* Set up the attributes to make the thread joinable, then start it. */
1058 if( (result = pthread_attr_init( attr )) )
1059 LogX( EXIT_FAILURE, "Failure initializing worker thread attributes; %s.\n",
1060 strerror( result ) );
1062 if( (result = pthread_attr_setdetachstate( attr, PTHREAD_CREATE_JOINABLE )) )
1064 "Failure setting \"joinable\" attribute for worker thread; %s.\n",
1065 strerror( result ) );
1067 if( (result = pthread_create( &WorkerThread, attr, WorkerMain, NULL )) )
1068 LogX( EXIT_FAILURE, "Failure spawning worker thread; %s.\n",
1069 strerror( result ) );
1073 static int OpenListenSock( const char *sockpath )
1074 /* ------------------------------------------------------------------------ **
1075 * Open a Unix Domain socket and set it to listen for connection requests.
1077 * Input: sockpath - The pathname of the Unix Domain socket to be
1078 * opened for listening.
1080 * Output: The opened and listening socket.
1081 * Only a valid value will be returned. This function will exit
1082 * the program on error.
1084 * Notes: We call this prior to spinning off any threads, so we can avoid
1085 * locking the global mutex.
1087 * ------------------------------------------------------------------------ **
1090 struct sockaddr_un laddr[1];
1094 * We must be able to fit the socket path into a Unix Domain socket address.
1096 if( strlen( sockpath ) >= sizeof( laddr->sun_path ) )
1099 "Annoyance: Path '%s' exceeds maximum unix domain address length.\n",
1103 /* Create a unix domain socket. */
1104 lsock = socket( AF_UNIX, SOCK_STREAM, 0 );
1108 "Failure: Unable to create unix domain socket; %s.\n", ErrStr );
1111 /* Bind the socket to the name within the namespace. */
1112 (void)unlink( sockpath );
1113 (void)memset( laddr, 0, sizeof( struct sockaddr_un ) );
1114 laddr->sun_family = AF_UNIX;
1115 (void)strcpy( laddr->sun_path, sockpath );
1116 if( bind( lsock, (struct sockaddr *)laddr, sizeof( laddr ) ) < 0 )
1118 (void)close( lsock );
1120 "Failure: Socket bind() to '%s' failed; %s.\n", sockpath, ErrStr );
1123 /* Set the socket to listen. */
1124 if( listen( lsock, 32 ) < 0 )
1126 (void)close( lsock );
1127 (void)unlink( sockpath );
1129 "Failure: Cannot listen on unix domain socket '%s'; %s.\n",
1134 } /* OpenListenSock */
1137 static void Spawn( void )
1138 /* ------------------------------------------------------------------------ **
1139 * Spawn a daemon process, which will take over control for us.
1144 * Notes: If the child process is created successfully, the parent
1145 * process is terminated using _exit(2). This bypasses executing
1146 * any functions registered with atexit(3) or on_exit(3). We
1147 * don't have any such functions but, even so, this is the normal
1148 * way to exit the parent process.
1149 * See: http://www.steve.org.uk/Reference/Unix/faq_2.html#SEC6
1151 * ------------------------------------------------------------------------ **
1156 if( pid < 0 ) /* Error. */
1157 LogX( EXIT_FAILURE, "Cannot spawn child process; %s\n", ErrStr );
1159 if( pid > 0 ) /* Parent process. */
1160 _exit( EXIT_SUCCESS );
1165 LogX( EXIT_FAILURE, "Failure: setsid(2) failed in Spawn(); %s.\n", ErrStr );
1167 Log( "Process %d spawned.\n", getpid() );
1168 /* Close standard file descriptors. */
1169 close( STDIN_FILENO );
1170 close( STDOUT_FILENO );
1171 close( STDERR_FILENO );
1176 static void ReadKeys( void )
1177 /* ------------------------------------------------------------------------ **
1178 * Read and store the server secret signing keys from the key files.
1183 * Notes: The signing key is officially known as the "Server Secret".
1184 * See the definition of Server Secret in [MS-PCCRC], section 1.1.
1186 * The "Server Secret" is the SHA-256 hash of "an arbitrary length
1187 * binary string stored on the server”. [MS-PCCRC] does not give
1188 * a name to this arbitrary binary string. In Prequel
1189 * documentation, it is referred to as the "Server Passphrase".
1191 * This function makes one or more passes through the sourcedir
1192 * configuration list. Each time, it finds the first entry
1193 * that has a NULL <userParam> pointer. It attempts to read the
1194 * signing key from the keyfile. If it succeeds, it goes
1195 * through the rest of the list looking for matching keyfile
1196 * names. If it finds a match, it points the <userParam> of
1197 * the matching entry to the already-copied key.
1199 * The userParam pointer could be used for storing a more complex
1200 * structure or other extended information. Right now, it's only
1201 * used for storing the signing key.
1203 * ------------------------------------------------------------------------ **
1207 pd_ConfigRec *entry;
1209 unsigned char *ss = NULL;
1215 forEach( entry, Cfg ) /* Read ConfigRec entries sequentially. */
1217 if( NULL == entry->userParam )
1219 if( NULL == kfName )
1221 kfName = entry->keyFileName;
1222 if( NULL == (ss = (unsigned char *)malloc( PD_V1_KEY_SIZE )) )
1224 "Failure: malloc(3) failed in ReadKeys(); %s.\n", ErrStr );
1225 if( NULL == (keyF = fopen( kfName, "r" )) )
1227 "Failure: Could not open keyfile %s; %s.\n", kfName, ErrStr );
1228 result = fread( ss, 1, PD_V1_KEY_SIZE, keyF );
1229 if( PD_V1_KEY_SIZE != result )
1231 "Failure: Only %d bytes in keyfile %s.\n", result, kfName );
1232 (void)fclose( keyF );
1233 entry->userParam = ss;
1237 if( 0 == strcmp( kfName, entry->keyFileName ) )
1238 entry->userParam = ss;
1242 } while( NULL != kfName );
1247 static void ReadConfig( char *fname )
1248 /* ------------------------------------------------------------------------ **
1249 * Open, read, and interpret the configuration file.
1251 * Input: fname - The name of the file to open, or NULL.
1252 * If <fname> is NULL, then the default file name will
1257 * Notes: The configuration file format was inspired, somewhat, by the
1258 * format of the ISC dhcpd.conf file.
1260 * ------------------------------------------------------------------------ **
1265 /* Ensure that we have a configurage file pathname,
1266 * then attempt to open the file for reading.
1269 fname = DEFAULT_CONFIG_FILE;
1270 if( NULL == (configF = fopen( fname, "r" )) )
1271 Fail( "Unable to open configuration file \"%s\" for input; %s.\n",
1275 * <Cfg> is a global pointer.
1277 Cfg = pd_ParseCfg( configF );
1285 static void ReadOpts( int argc, char *argv[] )
1286 /* ------------------------------------------------------------------------ **
1287 * Interpret any command-line options, and the configuration file.
1289 * Input: argc - Count of arguments.
1290 * argv - Array of pointers to argument strings.
1294 * Notes: This function also calls <ReadConfig()>, which reads and
1295 * interprets the configuration file.
1297 * This is quite a workhorse function. It handles collecting
1298 * and validating the configuration of the daemon.
1300 * ------------------------------------------------------------------------ **
1305 extern char *optarg;
1307 char *ConfigFile = NULL;
1308 char *LogFile = NULL;
1309 bool VerbSet = false;
1310 bool SpewHelp = false;
1311 bool SpewVersion = false;
1312 bool TestConfig = false;
1314 /* Read the arguments.
1316 while( (c = getopt( argc, argv, "c:fhl:qtvV" )) >= 0 )
1321 ConfigFile = optarg;
1340 if( Verbosity < 0xFF )
1348 /* Unknown option. Provide simple help, then exit.
1351 Usage( argv[0], EXIT_FAILURE );
1356 /* Provide version information (and more) if it was requested.
1360 Say( "%s\n", Revision );
1366 Say( "%s\n", Copyright );
1369 Say( "%s\n", License );
1372 Say( "Developed in participation with the " );
1373 Say( "Protocol Freedom Information Foundation.\n" );
1384 exit( EXIT_SUCCESS );
1387 /* If help was requested, send help.
1391 Usage( argv[0], EXIT_SUCCESS );
1394 /* If the user requested a configuration file test, provide it and
1395 * then exit the program.
1399 (void)setLogFile( stderr );
1400 if( NULL == ConfigFile )
1401 ConfigFile = DEFAULT_CONFIG_FILE;
1402 ReadConfig( ConfigFile );
1403 if( (1 == Verbosity) && (NULL != Cfg) )
1404 Log( "%s: successfully parsed.\n", ConfigFile );
1407 exit( EXIT_SUCCESS );
1411 * If a log file name was specified, then all messages from this point
1412 * on will be directed to the log file.
1413 * If no log file was specified, and we will run as a daemon, then we
1414 * start logging to syslog().
1415 * Otherwise, we log to <stderr> unless/until a log file name is given
1416 * in the configuration file.
1418 if( NULL == LogFile )
1421 OpenSyslog( SYSLOG_NAME );
1423 (void)setLogFile( stderr );
1427 LogF = fopen( LogFile, "a" );
1429 Fail( "Unable to open log file \"%s\" for output; %s.\n",
1431 (void)setLogFile( LogF );
1434 /* Open and read the configuration file.
1435 * Command-line options override configfile options. This is particularly
1436 * tricky when dealing with logging, which has already started.
1438 ReadConfig( ConfigFile );
1439 if( NULL == Cfg ) /* Parsing returned no configuration. Exit. */
1440 LogX( EXIT_FAILURE, "Null configuration. Exiting.\n" );
1442 if( NULL == LogFile )
1444 /* We have already started logging to syslog (background) or stderr
1445 * (foreground). If a log file was specified in the config file, and
1446 * if we are not running in the foreground, use the specified config
1449 if( (NULL != Cfg->logFileName) && Daemonize )
1451 LogF = fopen( Cfg->logFileName, "a" );
1453 Fail( "Unable to open log file \"%s\" for output; %s.\n",
1454 Cfg->logFileName, ErrStr );
1455 (void)setLogFile( LogF );
1459 /* When writing to <stderr>, don't print timestamps. */
1460 (void)setLogTimeStamp( false );
1465 /* If we have a command-line specified logfile, store it in the Cfg
1466 * structure for later use.
1468 Cfg->logFileName = replaceStr( LogFile, Cfg->logFileName );
1471 /* User-set verbosity overrides config file. */
1474 pd_ConfigRec *entry;
1476 forEach( entry, Cfg )
1477 entry->verbosity = (0xFF & Verbosity);
1483 /* -------------------------------------------------------------------------- **
1487 int main( int argc, char *argv[] )
1488 /* ------------------------------------------------------------------------ **
1491 * Input: argc - You know what this is.
1492 * argv - You know what to do.
1494 * Output: EXIT_SUCCESS or, on failure, EXIT_FAILURE.
1496 * ------------------------------------------------------------------------ **
1501 /* Gather working parameters.
1502 * Note that ReadOpts() takes care of reading the configuration file.
1504 ReadOpts( argc, argv ); /* Get all set up to run. */
1505 ReadKeys(); /* Find the signing keys. */
1507 /* Become a daemon, unless we were told otherwise. */
1511 /* Before creating any subthreads, let's try to open the listening socket. */
1512 listenSock = OpenListenSock( Cfg->sockFileName );
1514 /* Initialize the priority queue and start the worker thread. */
1515 (void)ubi_slInitList( WorkQueue );
1518 /* Listen for incoming requests on the named Unix Domain socket. */
1519 Listener( listenSock );
1521 /* When we stop listening, it's time to shut down the worker thread. */
1522 (void)pthread_mutex_lock( &GlobalMutex );
1524 (void)pthread_mutex_unlock( &GlobalMutex );
1525 (void)pthread_join( WorkerThread, NULL );
1527 /* Clean up and exit. */
1529 Log( "Normal exit.\n" );
1530 return( EXIT_SUCCESS );
1533 /* ========================================================================== */