1 /* ========================================================================== **
5 * Copyright (C) 2012 by Christopher R. Hertel
7 * Email: crh@ubiqx.mn.org
9 * $Id: PrequelD.c; 2014-09-22 17:14:11 -0500; Christopher R. Hertel$
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.. -D_FILE_OFFSET_BITS=64 -o PrequelD PrequelD.c \
64 * PD_peerdist1.c PD_read_config.c PD_sha2_oSSL.c PD_utils.c Gstr.c \
65 * ../ubi_sLinkList.c -lcrypto -lpthread
68 * + Add support for Unicode pathnames.
70 * + The background thread should traverse source directories and generate
71 * hash caches for any file that meets criteria and does not have an
72 * up-to-date hash cache file.
74 * + The background thread should traverse cache directories and delete
75 * any cache files that are out-of-date or which map back to a source
76 * file that no longer exists (or has been truncated to a size that is
77 * smaller than the minimum).
79 * + Add PeerDist v2 support.
81 * + Add signal handling:
82 * - HUP: Lock the mutex and re-read the config file. Update directory
84 * - TERM: Exit cleanly.
86 * + Long-term: Collect interesting statistics and log them on SIGHUP,
87 * SIGTERM, or SIGUSR1.
89 * + Add a -s <socket> option to override the socket path in the config
92 * + More consideration should be given to the handling of the "Server
93 * Secret", which is used to sign the "Segment Hash of Data" to create
94 * the "Segment Secret". At present, we just read the "Server Secret"
95 * from a file. See the <ReadKeys()> function, below.
98 * - Each sourcedir *may* have its own keyfile. One of the options for
99 * the keyfile (possibly the default?) would be to generate a Server
100 * Secret on the fly. In that case, the Server Secret would be
101 * different every time PrequelD is run (which wouldn't be all bad,
102 * assuming PrequelD is really stable). It would mean that caches
103 * would be invalidated every time PrequelD restarts, and that the
104 * key could not be shared by multiple servers. Providing this option
105 * would further require that we allow for an empty keyfile value in
106 * the config file (which might change the syntax).
107 * - Server Secrets could be stored AES encrypted, to match the format
108 * of Server Secrets extracted from Windows servers. The problem
109 * with this option is that PrequelD would still need to have access
110 * to the passphrase used to decrypt the AES-encrypted secrets, so
111 * we would still have an exposed password.
115 * + The 'exclude' configuration command doesn't yet work.
117 * + Need to detect whether we are running already or not. We can run
118 * multiple daemons at once, as long as their src directories don't
119 * overlap and they are using different sockets. Add a PID file path
120 * to the configuration, default to /var/run/prequel.pid
122 * + We implement a QUERY command over the Unix domain socket. Its purpose
123 * is to return the pathname of a cache file given the pathname of the
126 * Problem is, the current implementation of the QUERY does not include
127 * the PeerDist major/minor numbers, so we can only return the least
128 * common denominator (1.0). The QUERY command needs to be changed to
129 * pass version numbers, or to *optionally* pass version numbers when
130 * the least common denominator is not being requested.
132 * ========================================================================== **
135 #include <time.h> /* For nanosleep(2). */
136 #include <stdlib.h> /* Standard Library. */
137 #include <stdbool.h> /* Standard boolean type. */
138 #include <stdint.h> /* Standard extended integer types. */
139 #include <unistd.h> /* For getopt(3), close(2), unlink(2), etc. */
140 #include <string.h> /* For strerror(3), strchr(3). */
141 #include <stdarg.h> /* Standard variable argument lists. */
142 #include <ctype.h> /* For isspace(3) and iscntrl(3). */
143 #include <sys/types.h> /* For the pid_t and DIR* types. */
144 #include <dirent.h> /* For reading directory entries. */
145 #include <pthread.h> /* For POSIX Threads. */
146 #include <poll.h> /* For ppoll(2) and struct pollfd. */
147 #include <sys/socket.h> /* Socket(7) interface header. */
148 #include <sys/un.h> /* Unix(7) domain sockets. */
149 #include <sys/time.h> /* The timeval struct and gettimeofday(2). */
151 #include "PD_peerdist1.h" /* PeerDist v1 protocol support. */
152 #include "PD_read_config.h" /* Read the configuration file. */
153 #include "PD_utils.h" /* General PrequelD utilities. */
154 #include "ubi_sLinkList.h" /* Linked List module. */
155 #include "Gstr.h" /* Growable string buffers. */
158 /* -------------------------------------------------------------------------- **
161 * DEFAULT_CONFIG_FILE - Default pathname of the configuration file to read
162 * on startup. This value can be overridden at
165 * SYSLOG_NAME - The name used to identify this program in Syslog
168 * RECV_TIMEOUT - Number of seconds and microseconds to listen on
169 * an input socket while receiving data. Formated
170 * as an initialization pair: { <sec>, <usec> }.
171 * 1usec = 1/1000000sec (10^-6, or 1/1000msec)
173 * POLL_TIMEOUT - Number of milliseconds to wait when poll(2)ing
174 * an input socket. 1msec = 1/1000sec == 1000usec.
176 * bSIZE - A buffer size used to allocate the read buffer
177 * included in the <RecvBufr> structure.
180 #ifndef DEFAULT_CONFIG_FILE
181 #define DEFAULT_CONFIG_FILE "/etc/prequeld/pd.conf"
184 #define SYSLOG_NAME "prequeld"
186 #define RECV_TIMEOUT { 2, 0 } /* 2 seconds. */
187 #define POLL_TIMEOUT 30000 /* 30 seconds. */
192 /* -------------------------------------------------------------------------- **
195 * forEach( V, L ) - A macro for traversing a linked list of <pd_ConfigRec>
196 * records. This is just a simplified way of expressing a
197 * linked-list traversal using a "for" loop. Think of it
198 * as "for each <V> in <L>".
199 * V - The variable that will point to each node in the
200 * list. <V> must be substituted with a pointer to a
201 * <pd_ConfigRec> structure.
202 * L - The list to be traversed.
206 #define forEach( V, L ) \
207 for( (V) = (pd_ConfigRec *)ubi_slFirst( (L) ); \
209 (V) = (pd_ConfigRec *)ubi_slNext( (V) ) )
212 /* -------------------------------------------------------------------------- **
215 * RangeVal - Used to read and interpret the value of the Range: option,
216 * which may be sent by the client. If <given> is false, the
217 * we know that the client did not provide a value. If <given>
218 * is true, then the client did provide a value and that value
219 * is stored in the <val> field.
221 * WorkItem - A queue entry and a string pointer pointing to the pathname
222 * of a source file that is in need of hashing.
228 bool given; /* Was a value given. */
229 uint64_t val; /* The given value. */
234 ubi_slNode node; /* Linked list node. */
235 char *srcPath; /* Source file path. */
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; 2014-09-22 17:14:11 -0500; Christopher R. Hertel$";
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
323 * of the real work (that is, generating and maintaining
324 * hash files). The "foreground" thread communicates with
327 * GlobalMutex - A global mutex attribute. The worker thread needs brief
328 * access to the <Cfg> list and other state attribues.
329 * This allows the foreground and background to share.
331 * Shutdown - Used to let the worker process know when it is time to
332 * shut down and return.
334 * WorkQueue - A queue of source file names that are waiting to be
335 * hashed. The queue is filled when a request is made
336 * and no hash cache file is found. It is emptied by
337 * the worker thread. Queued entries have priority over
338 * source files found during directory traversal.
341 static unsigned int Verbosity = 1;
342 static bool Daemonize = true;
343 static pd_Config *Cfg = NULL;
344 static pthread_t WorkerThread;
345 static pthread_mutex_t GlobalMutex = PTHREAD_MUTEX_INITIALIZER;
346 static bool Shutdown = false;
347 static ubi_slList WorkQueue[1];
350 /* -------------------------------------------------------------------------- **
354 static void Usage( const char *progname, int status )
355 /* ------------------------------------------------------------------------ **
356 * Provide usage information, then exit.
358 * Input: progname - Pointer to a string containing the program name.
359 * status - Exit status to return.
363 * Notes: Output is sent to <stdout> so that it can be piped through
364 * 'more' or 'less' or something else.
366 * ------------------------------------------------------------------------ **
370 const char **msg = (Verbosity > 1) ? VerboseMsg : HelpMsg;
372 Say( "Usage: %s [options]\n", progname );
373 for( i = 0; NULL != msg[i]; i++ )
374 Say( "%s\n", msg[i] );
380 static void LockLog( char *fmt, ... )
381 /* ------------------------------------------------------------------------ **
382 * Lock/unlock the global mutex to safely write a log message.
384 * Input: fmt - Format string, as used in printf(), etc.
385 * ... - Variable parameter list.
389 * Notes: A simple wrapper around <vLog()> in PD_utils.
391 * See: <PD_utils.vLog()>
393 * ------------------------------------------------------------------------ **
398 if( 0 == pthread_mutex_lock( &GlobalMutex ) )
403 (void)pthread_mutex_unlock( &GlobalMutex );
408 static void deltaTval( struct timeval *tval )
409 /* ------------------------------------------------------------------------ **
410 * Calculate the difference between then and now.
412 * Input: tval - A pointer to a timeval structure containing an initial
413 * time (start time). The contents of <tval> will be
414 * updated to indicate the difference in time between
415 * the initial time and the current time.
419 * Notes: If the contents of <tval> are all zero or <tval> is ahead of
420 * the current time, the input is considered to be invalid and
421 * <tval> will be returned with all zeros to indicate the error.
423 * See: gettimeofday(2)
425 * ------------------------------------------------------------------------ **
428 struct timeval now_tv[1];
429 struct timeval result = { 0, 0 };
431 /* Is the input valid? */
432 if( (0 != tval->tv_sec) || (0 != tval->tv_usec) )
434 /* Attempt to get the current time. */
435 if( 0 == gettimeofday( now_tv, NULL ) )
437 /* <now_tv> must be greater than <tval>. */
438 if( (now_tv->tv_sec > tval->tv_sec)
439 || ((now_tv->tv_sec == tval->tv_sec)
440 && (now_tv->tv_usec > tval->tv_usec)) )
442 result.tv_sec = now_tv->tv_sec - tval->tv_sec;
443 if( now_tv->tv_usec > tval->tv_usec )
444 result.tv_usec = now_tv->tv_usec - tval->tv_usec;
448 result.tv_usec = (1000000 - tval->tv_usec) + now_tv->tv_usec;
462 static void *Responder( void *sockptr )
463 /* ------------------------------------------------------------------------ **
464 * Respond to requests from a client.
466 * Input: sockptr - A pointer to an integer, which is the open
467 * communications socket.
471 * Notes: This is, essentially, the mainline of a thread that is
472 * spawned to deal with a single client connection. It
473 * handles requests from the client until the client closes
476 * See: The <Readme.txt> file includes the definition of the simple
477 * client/server protocol.
479 * ------------------------------------------------------------------------ **
482 int client_sock = *((int *)thingy); /* Copy the socked id. */
483 bool run = true; /* Ready to run. */
484 struct timeval tv[1] = { RECV_TIMEOUT };
486 struct pollfd pfd[1];
489 /* Free unused memory and detach the thread so we can just exit when done. */
491 (void)pthread_detach( pthread_self() );
493 /* Log entry to let the world know we're running. */
495 LockLog( "Responder() running to handle a new connection.\n" );
497 /* Set a timeout on the i/o socket. */
498 if( setsockopt( client_sock, SOL_SOCKET, SO_RCVTIMEO,
499 (void *)tv, sizeof( struct timeval ) ) )
501 /* This should never happen. */
502 LockLog( "Error setting timeout on socket; %s.\n", ErrStr );
506 /* Read and handle request messages, one at a time. */
511 /* All done. Close the socket and the thread. */
512 (void)close( client_sock );
517 static void Listener( const int lsock )
518 /* ------------------------------------------------------------------------ **
519 * Listen for incomming connection requests and spawn responder threads.
521 * Input: lsock - Listening socket.
525 * Notes: This function is run within the primary thread, but it is
526 * started after the worker thread has been spun off so it
527 * must lock the global mutex before accessing any commonly
530 * We allocate an integer from the heap, copy the socket number,
531 * and pass the pointer to the child. This is done in part to
532 * avoid an annoying problem. On 64-bit systems (it seems) the
533 * integer type is a different size than the void * type. Doing
534 * things the old fashioned way (casting the int to void * and
535 * then casting back within the child) generates a legitimate
536 * warning. Casting between different size types may not work
537 * as we would expect.
539 * As long as we can get the integer from memory, this method is
540 * more 'correct'... sort of.
542 * See Also: <Responder()>
544 * ------------------------------------------------------------------------ **
550 pthread_t threadhandle;
552 /* Wait for connection requests, then spawn off threads to handle the
553 * incoming connections.
555 while( (newSock = accept( lsock, NULL, NULL )) >= 0 )
557 parptr = (int *)malloc( sizeof( int ) );
561 result = pthread_create( &threadhandle, NULL, &Responder, parptr );
563 if( (NULL == parptr) || result ) /* Check for problems. */
565 if( 0 == pthread_mutex_lock( &GlobalMutex ) )
570 "Failure: Memory allocation failure creating subthread.\n" );
574 /* We free <parptr> here because the subthread was not created.
575 * Normally, <parptr> would be freed by the subthread.
577 Log( "Warning: Error creating responder subthread; %s.\n",
578 strerror( result ) );
581 (void)pthread_mutex_unlock( &GlobalMutex );
583 (void)close( newSock );
587 /* Lock the mutex to write the error message to the log.
588 * To get here, there must have been an error returned by <accept()>.
590 if( 0 == pthread_mutex_lock( &GlobalMutex ) )
592 /* The pthread lock does not modify errno, so we don't need to store it. */
593 Log( "Failure: Error accepting socket connection; %s.\n", ErrStr );
594 (void)pthread_mutex_unlock( &GlobalMutex );
600 static void WorkHash( const char *srcPathName )
601 /* ------------------------------------------------------------------------ **
602 * Create a hash cache file given a source file name.
604 * Input: srcPathName - The pathname of the source file to be hashed.
608 * Notes: Dancing with the mutex.
610 * ------------------------------------------------------------------------ **
613 pd_ConfigRec *entry; /* Configuration record pointer. */
614 char *cacheDir; /* Copy of the cacheDir path. */
615 int minBlocks; /* Minimum source file block size. */
616 pd_v1HashType hashType; /* Type of hash to be used. */
617 unsigned char key[PD_V1_KEY_SIZE]; /* SourceDir's Server Secret. */
618 int result; /* Store function call return val. */
619 int syserr; /* Store returned system error val. */
620 struct timeval tval[1]; /* For calculating hash times. */
622 /* Try to grab the mutex.
623 * If we can't grab the mutex, then something's wrong but we cannot log it
624 * because we need the mutex in order to (safely) write to the log. We'll
627 if( 0 != pthread_mutex_lock( &GlobalMutex ) )
631 * Find the config record that points to the source directory tree in
632 * which the source file resides.
634 entry = pd_FindSrcDir( Cfg, srcPathName );
638 Log( "Queued file is not in scope: %s\n", srcPathName );
639 (void)pthread_mutex_unlock( &GlobalMutex );
642 /* This should be impossible.
643 * Check PD_read_config if this ever happens.
645 if( NULL == (entry->cacheDir) )
647 Log( "Warning: Bad configuration entry; cacheDir is NULL.\n" );
648 (void)pthread_mutex_unlock( &GlobalMutex );
652 /* We have mutex and a configuration entry.
653 * Copy all of the config values so that we can release the mutex.
655 cacheDir = strdup( entry->cacheDir );
656 minBlocks = entry->minBlocks;
657 hashType = entry->v1HashType;
659 (void)memcpy( key, entry->userParam, PD_V1_KEY_SIZE );
660 /* Unlock the mutex. */
661 (void)pthread_mutex_unlock( &GlobalMutex );
663 /* Check that the <cacheDir> copy was successfully created. */
664 if( NULL == cacheDir )
667 Log( "Warning: Memory allocation failure in WorkHash().\n" );
671 /* If anyone knows any reason why this hash file should not be created,
674 result = pd_v1ShouldHash( srcPathName, cacheDir, minBlocks );
675 if( pd_Success != result )
679 Log( "Won't hash %s; %s.\n", srcPathName,
680 pd_v1StrResult( result ) );
686 /* If we're going to output timing, try to get the start time.
687 * Set the start time to zero if the gettimeofday(2) call fails.
689 if( (Verbosity > 2) && (0 != gettimeofday( tval, NULL )) )
690 tval->tv_sec = tval->tv_usec = 0;
692 /* Now do the hashing. */
693 result = pd_v1CreateHashCache( srcPathName,
698 /* Optionally generate the delta time. */
704 /* If we're not going to (or can't) report anything, we're done. */
705 if( (Verbosity < 1) || (0 != pthread_mutex_lock( &GlobalMutex )) )
708 /* Report the results.
709 * It is not a fatal error to fail to create the cache file, but it should
710 * probably be logged. Logging requires the mutex.
712 if( pd_Success == result )
716 if( (0 != tval->tv_sec) || (0 !=tval->tv_usec) )
718 int m = ((tval->tv_sec) / 60);
719 int s = ((tval->tv_sec) - (m * 60));
720 int ms = ((tval->tv_usec) / 1000);
722 if( ((tval->tv_usec) - (ms * 1000)) >= 500 )
724 Log( " Hashed: %s in %0dm%0d.%0.3ds.\n", srcPathName, m, s, ms );
727 Log( " Hashed: %s.\n", srcPathName );
733 Log( "Failure hashing %s; %s; %s.\n", srcPathName,
734 pd_v1StrResult( result ), strerror( syserr ) );
736 Log( "Failure hashing %s; %s.\n", srcPathName,
737 pd_v1StrResult( result ) );
740 /* And release the mutex again. */
741 (void)pthread_mutex_unlock( &GlobalMutex );
745 static void *WorkerMain( void *thingy )
746 /* ------------------------------------------------------------------------ **
747 * Worker thread mainline.
749 * Input: thingy - Pointer to an opaque blob that currently isn't used.
750 * This is the <void *arg> parameter to
753 * Output: Always returns NULL.
755 * Notes: This function is the mainline of a background thread. It's
756 * job is to generate the hash cache files.
758 * FIX: The wDir state is designed to allow the worker thread to
759 * traverse configured source directories and hash files that
760 * have not been requested yet. The requested files, listed
761 * in the <WorkQueue> have higher priority. Unfortunately,
762 * directory traversal has not been implemented yet.
764 * In addition, we should traverse the cache directories and
765 * purge cache files that are out of date or for which the
766 * source file no longer exists.
768 * ------------------------------------------------------------------------ **
771 static enum { wNone, wStop, wQueue, wDir } doState; /* What shall we do? */
772 static struct timespec ts = { 0, 500000000 }; /* Half a second. */
773 WorkItem *srcEntry = NULL;
777 /* Start our loop by waiting a half second, just to pace ourselves. */
778 (void)nanosleep( &ts, NULL );
780 /* Determine which action to take, and collect any required data.
781 * This requires locking/unlocking the global mutex.
783 doState = wNone; /* Default action. */
784 if( 0 == pthread_mutex_lock( &GlobalMutex ) )
786 /* Note: See pthread_mutex_lock(3). We do not check for an error
787 * because none of the listed possibilities are at all likely
788 * to occurr and, if an error were to occur, we don't have a
789 * sensible way of handling it (other than to wait and retry).
795 if( 0 == ubi_slCount( WorkQueue ) )
799 srcEntry = (WorkItem *)ubi_slDequeue( WorkQueue );
800 if( (NULL != srcEntry) && (NULL != srcEntry->srcPath) )
804 Log( "Dequeued: %s\n", srcEntry->srcPath );
807 Log( "Error: NULL entry in priority queue.\n" );
810 (void)pthread_mutex_unlock( &GlobalMutex );
813 /* Now do the thing. */
817 /* Hash an entry from the queue, then free the entry as a whole. */
820 WorkHash( srcEntry->srcPath );
825 /* Hash an entry from a source directory. */
826 /* FIX: Need directory traversal. */
832 } while( wStop != doState );
838 static void StartWorker( void )
839 /* ------------------------------------------------------------------------ **
840 * Start a background thread to generate hash cache files.
845 * Notes: The worker thread does the work of creating hash cache files
846 * from source content. It runs separately so that it does not
847 * interfere with communication with our clients.
849 * <WorkerThread> is a global variable.
850 * <WorkerMain()> is is the "mainline" of the worker thread.
852 * ------------------------------------------------------------------------ **
856 pthread_attr_t attr[1];
858 /* Set up the attributes to make the thread joinable, then start it. */
859 if( (result = pthread_attr_init( attr )) )
860 LogX( EXIT_FAILURE, "Failure initializing worker thread attributes; %s.\n",
861 strerror( result ) );
863 if( (result = pthread_attr_setdetachstate( attr, PTHREAD_CREATE_JOINABLE )) )
865 "Failure setting \"joinable\" attribute for worker thread; %s.\n",
866 strerror( result ) );
868 if( (result = pthread_create( &WorkerThread, attr, WorkerMain, NULL )) )
869 LogX( EXIT_FAILURE, "Failure spawning worker thread; %s.\n",
870 strerror( result ) );
874 static int OpenListenSock( const char *sockpath )
875 /* ------------------------------------------------------------------------ **
876 * Open a Unix Domain socket and set it to listen for connection requests.
878 * Input: sockpath - The pathname of the Unix Domain socket to be
879 * opened for listening.
881 * Output: The opened and listening socket.
882 * Only a valid value will be returned. On error, this function
883 * will exit the program.
885 * Notes: We call this prior to spinning off any threads, so we can avoid
886 * locking the global mutex.
888 * ------------------------------------------------------------------------ **
891 struct sockaddr_un laddr[1];
895 * We must be able to fit the socket path into a Unix Domain socket address.
897 if( strlen( sockpath ) >= sizeof( laddr->sun_path ) )
900 "Annoyance: Path '%s' exceeds maximum unix domain address length.\n",
904 /* Create a unix domain socket. */
905 lsock = socket( AF_UNIX, SOCK_STREAM, 0 );
909 "Failure: Unable to create unix domain socket; %s.\n", ErrStr );
912 /* Bind the socket to the name within the namespace. */
913 (void)unlink( sockpath );
914 (void)memset( laddr, 0, sizeof( struct sockaddr_un ) );
915 laddr->sun_family = AF_UNIX;
916 (void)strcpy( laddr->sun_path, sockpath );
917 if( bind( lsock, (struct sockaddr *)laddr, sizeof( laddr ) ) < 0 )
919 (void)close( lsock );
921 "Failure: Socket bind() to '%s' failed; %s.\n", sockpath, ErrStr );
924 /* Set the socket to listen. */
925 if( listen( lsock, 32 ) < 0 )
927 (void)close( lsock );
928 (void)unlink( sockpath );
930 "Failure: Cannot listen on unix domain socket '%s'; %s.\n",
935 } /* OpenListenSock */
938 static void Spawn( void )
939 /* ------------------------------------------------------------------------ **
940 * Spawn a daemon process, which will take over control for us.
945 * Notes: This function causes the program to run in the background.
947 * If the child process is created successfully, the parent
948 * process is terminated using _exit(2). This bypasses executing
949 * any functions registered with atexit(3) or on_exit(3). We
950 * don't have any such functions but, even so, this is the normal
951 * way to exit the parent process.
952 * See: http://www.steve.org.uk/Reference/Unix/faq_2.html#SEC6
954 * ------------------------------------------------------------------------ **
959 if( pid < 0 ) /* Error. */
960 LogX( EXIT_FAILURE, "Cannot spawn child process; %s\n", ErrStr );
962 if( pid > 0 ) /* Parent process; just exit. */
963 _exit( EXIT_SUCCESS );
968 LogX( EXIT_FAILURE, "Failure: setsid(2) failed in Spawn(); %s.\n", ErrStr );
970 Log( "Process %d spawned.\n", getpid() );
971 /* Close standard file descriptors. */
972 close( STDIN_FILENO );
973 close( STDOUT_FILENO );
974 close( STDERR_FILENO );
979 static void ReadKeys( void )
980 /* ------------------------------------------------------------------------ **
981 * Read and store the server secret signing keys from the key files.
986 * Notes: The signing key is officially known as the "Server Secret".
987 * See the definition of Server Secret in [MS-PCCRC], section 1.1.
989 * The "Server Secret" is the SHA-256 hash of "an arbitrary length
990 * binary string stored on the server”. [MS-PCCRC] does not give
991 * a name to this arbitrary binary string. In Prequel
992 * documentation, it is referred to as the "Server Passphrase".
993 * So, the Server Secret is the hash of the Server Passphrase.
995 * This function makes one or more passes through the sourcedir
996 * configuration list. Each time, it finds the first entry
997 * that has a NULL <userParam> pointer. It attempts to read the
998 * signing key from the keyfile. If it succeeds, it goes
999 * through the rest of the list looking for matching keyfile
1000 * names. If it finds a match, it points the <userParam> of
1001 * the matching entry to the already-copied key.
1003 * The userParam pointer could be used for storing a more complex
1004 * structure or other extended information. Right now, it's only
1005 * used for storing the signing key.
1007 * ------------------------------------------------------------------------ **
1011 pd_ConfigRec *entry;
1013 unsigned char *ss = NULL;
1019 forEach( entry, Cfg ) /* Read ConfigRec entries sequentially. */
1021 if( NULL == entry->userParam )
1023 if( NULL == kfName )
1025 kfName = entry->keyFileName;
1026 if( NULL == (ss = (unsigned char *)malloc( PD_V1_KEY_SIZE )) )
1028 "Failure: malloc(3) failed in ReadKeys(); %s.\n", ErrStr );
1029 if( NULL == (keyF = fopen( kfName, "r" )) )
1031 "Failure: Could not open keyfile %s; %s.\n", kfName, ErrStr );
1032 result = fread( ss, 1, PD_V1_KEY_SIZE, keyF );
1033 if( PD_V1_KEY_SIZE != result )
1035 "Failure: Only %d bytes in keyfile %s.\n", result, kfName );
1036 (void)fclose( keyF );
1037 entry->userParam = ss;
1041 if( 0 == strcmp( kfName, entry->keyFileName ) )
1042 entry->userParam = ss;
1046 } while( NULL != kfName );
1051 static void ReadConfig( char *fname )
1052 /* ------------------------------------------------------------------------ **
1053 * Open, read, and interpret the configuration file.
1055 * Input: fname - The name of the file to open, or NULL.
1056 * If <fname> is NULL, then the default file name will
1061 * Notes: The configuration file format was inspired, somewhat, by the
1062 * format of the ISC dhcpd.conf file.
1064 * ------------------------------------------------------------------------ **
1069 /* Ensure that we have a configurage file pathname,
1070 * then attempt to open the file for reading.
1073 fname = DEFAULT_CONFIG_FILE;
1074 if( NULL == (configF = fopen( fname, "r" )) )
1075 Fail( "Unable to open configuration file \"%s\" for input; %s.\n",
1079 * <Cfg> is a global pointer.
1081 Cfg = pd_ParseCfg( configF );
1089 static void ReadOpts( int argc, char *argv[] )
1090 /* ------------------------------------------------------------------------ **
1091 * Interpret any command-line options, and the configuration file.
1093 * Input: argc - Count of arguments.
1094 * argv - Array of pointers to argument strings.
1098 * Notes: This function also calls <ReadConfig()>, which reads and
1099 * interprets the configuration file.
1101 * This is quite a workhorse function. It handles collecting
1102 * and validating the configuration of the daemon.
1104 * ------------------------------------------------------------------------ **
1109 extern char *optarg;
1111 char *ConfigFile = NULL;
1112 char *LogFile = NULL;
1113 bool VerbSet = false;
1114 bool SpewHelp = false;
1115 bool SpewVersion = false;
1116 bool TestConfig = false;
1118 /* Read the arguments.
1120 while( (c = getopt( argc, argv, "c:fhl:qtvV" )) >= 0 )
1125 ConfigFile = optarg;
1144 if( Verbosity < 0xFF )
1152 /* Unknown option. Provide simple help, then exit.
1155 Usage( argv[0], EXIT_FAILURE );
1160 /* Provide version information (and more) if it was requested.
1164 Say( "%s\n", Revision );
1170 Say( "%s\n", Copyright );
1173 Say( "%s\n", License );
1176 Say( "Developed in participation with the " );
1177 Say( "Protocol Freedom Information Foundation.\n" );
1188 exit( EXIT_SUCCESS );
1191 /* If help was requested, send help.
1195 Usage( argv[0], EXIT_SUCCESS );
1198 /* If the user requested a configuration file test, provide it and
1199 * then exit the program.
1203 (void)setLogFile( stderr );
1204 if( NULL == ConfigFile )
1205 ConfigFile = DEFAULT_CONFIG_FILE;
1206 ReadConfig( ConfigFile );
1207 if( (Verbosity == 1) && (NULL != Cfg) )
1208 Log( "%s: successfully parsed.\n", ConfigFile );
1211 exit( EXIT_SUCCESS );
1215 * If a log file name was specified, then all messages from this point
1216 * on will be directed to the log file.
1217 * If no log file was specified, and we will run as a daemon, then we
1218 * start logging to syslog().
1219 * Otherwise, we log to <stderr> unless/until a log file name is given
1220 * in the configuration file.
1222 if( NULL == LogFile )
1225 OpenSyslog( SYSLOG_NAME );
1227 (void)setLogFile( stderr );
1231 LogF = fopen( LogFile, "a" );
1233 Fail( "Unable to open log file \"%s\" for output; %s.\n",
1235 (void)setLogFile( LogF );
1238 /* Open and read the configuration file.
1239 * Command-line options override configfile options. This is particularly
1240 * tricky when dealing with logging, which has already started.
1242 ReadConfig( ConfigFile );
1243 if( NULL == Cfg ) /* Parsing returned no configuration. Exit. */
1244 LogX( EXIT_FAILURE, "Null configuration. Exiting.\n" );
1246 if( NULL == LogFile )
1248 /* We have already started logging to syslog (background) or stderr
1249 * (foreground). If a log file was specified in the config file, and
1250 * if we are not running in the foreground, use the specified config
1253 if( (NULL != Cfg->logFileName) && Daemonize )
1255 LogF = fopen( Cfg->logFileName, "a" );
1257 Fail( "Unable to open log file \"%s\" for output; %s.\n",
1258 Cfg->logFileName, ErrStr );
1259 (void)setLogFile( LogF );
1263 /* When writing to <stderr>, don't print timestamps. */
1264 (void)setLogTimeStamp( false );
1269 /* If we have a command-line specified logfile, store it in the Cfg
1270 * structure for later use.
1272 Cfg->logFileName = replaceStr( LogFile, Cfg->logFileName );
1275 /* User-set verbosity overrides config file. */
1278 pd_ConfigRec *entry;
1280 forEach( entry, Cfg )
1281 entry->verbosity = (0xFF & Verbosity);
1287 /* -------------------------------------------------------------------------- **
1291 int main( int argc, char *argv[] )
1292 /* ------------------------------------------------------------------------ **
1295 * Input: argc - You know what this is.
1296 * argv - You know what to do.
1298 * Output: EXIT_SUCCESS or, on failure, EXIT_FAILURE.
1300 * ------------------------------------------------------------------------ **
1305 /* Gather working parameters.
1306 * Note that ReadOpts() also takes care of reading the configuration file.
1308 ReadOpts( argc, argv ); /* Get all set up to run. */
1309 ReadKeys(); /* Find the signing keys. */
1311 /* Become a daemon, unless we were told otherwise. */
1315 /* Before creating any subthreads, let's try to open the listening socket. */
1316 listenSock = OpenListenSock( Cfg->sockFileName );
1318 /* Initialize the priority queue and start the worker thread. */
1319 (void)ubi_slInitList( WorkQueue );
1322 /* Listen for incoming requests on the named Unix Domain socket. */
1323 Listener( listenSock );
1325 /* When we stop listening, it's time to shut down the worker thread. */
1326 (void)pthread_mutex_lock( &GlobalMutex );
1328 (void)pthread_mutex_unlock( &GlobalMutex );
1329 (void)pthread_join( WorkerThread, NULL );
1331 /* Clean up and exit. */
1333 Log( "Normal exit.\n" );
1334 return( EXIT_SUCCESS );
1337 /* ========================================================================== */