]> granicus.if.org Git - imagemagick/blob - MagickCore/distribute-cache.c
(no commit message)
[imagemagick] / MagickCore / distribute-cache.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %    DDDD    IIIII   SSSSS  TTTTT  RRRR   IIIII  BBBB   U   U  TTTTT  EEEEE   %
6 %    D   D     I     SS       T    R   R    I    B   B  U   U    T    E       %
7 %    D   D     I      SSS     T    RRRR     I    BBBB   U   U    T    EEE     %
8 %    D   D     I        SS    T    R R      I    B   B  U   U    T    E       %
9 %    DDDDA   IIIII   SSSSS    T    R  R   IIIII  BBBB    UUU     T    EEEEE   %
10 %                                                                             %
11 %                      CCCC   AAA    CCCC  H   H  EEEEE                       %
12 %                     C      A   A  C      H   H  E                           %
13 %                     C      AAAAA  C      HHHHH  EEE                         %
14 %                     C      A   A  C      H   H  E                           %
15 %                      CCCC  A   A   CCCC  H   H  EEEEE                       %
16 %                                                                             %
17 %                                                                             %
18 %                 MagickCore Distributed Pixel Cache Methods                  %
19 %                                                                             %
20 %                              Software Design                                %
21 %                                John Cristy                                  %
22 %                                January 2013                                 %
23 %                                                                             %
24 %                                                                             %
25 %  Copyright 1999-2013 ImageMagick Studio LLC, a non-profit organization      %
26 %  dedicated to making software imaging solutions freely available.           %
27 %                                                                             %
28 %  You may not use this file except in compliance with the License.  You may  %
29 %  obtain a copy of the License at                                            %
30 %                                                                             %
31 %    http://www.imagemagick.org/script/license.php                            %
32 %                                                                             %
33 %  Unless required by applicable law or agreed to in writing, software        %
34 %  distributed under the License is distributed on an "AS IS" BASIS,          %
35 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
36 %  See the License for the specific language governing permissions and        %
37 %  limitations under the License.                                             %
38 %                                                                             %
39 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
40 %
41 % A distributed pixel cache is an extension of the traditional pixel cache
42 % available on a single host.  The distributed pixel cache may span multiple
43 % servers so that it can grow in size and transactional capacity to support
44 % very large images.  Start up the pixel cache server on one or more machines.
45 % When you read or operate on an image and the local pixel cache resources are
46 % exhausted, ImageMagick contacts one or more of these remote pixel servers to
47 % store or retrieve pixels.
48 %
49 */
50 \f
51 /*
52   Include declarations.
53 */
54 #include "MagickCore/studio.h"
55 #include "MagickCore/cache.h"
56 #include "MagickCore/cache-private.h"
57 #include "MagickCore/distribute-cache.h"
58 #include "MagickCore/distribute-cache-private.h"
59 #include "MagickCore/exception.h"
60 #include "MagickCore/exception-private.h"
61 #include "MagickCore/geometry.h"
62 #include "MagickCore/image.h"
63 #include "MagickCore/locale_.h"
64 #include "MagickCore/memory_.h"
65 #include "MagickCore/policy.h"
66 #include "MagickCore/random_.h"
67 #include "MagickCore/registry.h"
68 #include "MagickCore/splay-tree.h"
69 #include "MagickCore/string_.h"
70 #include "MagickCore/string-private.h"
71 #if defined(MAGICKCORE_HAVE_SOCKET)
72 #include <netinet/in.h>
73 #include <netdb.h>
74 #include <sys/socket.h>
75 #include <arpa/inet.h>
76 #endif
77 \f
78 /*
79   Define declarations.
80 */
81 #define DPCHostname  "127.0.0.1"
82 #define DPCPort  6668
83 #define DPCSessionKeyLength  8
84 \f
85 /*
86 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
87 %                                                                             %
88 %                                                                             %
89 %                                                                             %
90 +   A c q u i r e D i s t r i b u t e C a c h e I n f o                       %
91 %                                                                             %
92 %                                                                             %
93 %                                                                             %
94 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
95 %
96 %  AcquireDistributeCacheInfo() allocates the DistributeCacheInfo structure.
97 %
98 %  The format of the AcquireDistributeCacheInfo method is:
99 %
100 %      DistributeCacheInfo *AcquireDistributeCacheInfo(ExceptionInfo *exception)
101 %
102 %  A description of each parameter follows:
103 %
104 %    o exception: return any errors or warnings in this structure.
105 %
106 */
107
108 static MagickSizeType CRC64(const unsigned char *message,const size_t length)
109 {
110   MagickSizeType
111     crc;
112
113   register ssize_t
114     i;
115
116   static MagickBooleanType
117     crc_initial = MagickFalse;
118
119   static MagickSizeType
120     crc_xor[256];
121
122   if (crc_initial == MagickFalse)
123     {
124       MagickSizeType
125         alpha;
126
127       for (i=0; i < 256; i++)
128       {
129         register ssize_t
130           j;
131
132         alpha=(MagickSizeType) i;
133         for (j=0; j < 8; j++)
134         {
135           if ((alpha & 0x01) == 0)
136             alpha>>=1;
137           else
138             alpha=(MagickSizeType) ((alpha >> 1) ^
139               MagickULLConstant(0xd800000000000000));
140         }
141         crc_xor[i]=alpha;
142       }
143       crc_initial=MagickTrue;
144     }
145   crc=0;
146   for (i=0; i < (ssize_t) length; i++)
147     crc=crc_xor[(crc ^ message[i]) & 0xff] ^ (crc >> 8);
148   return(crc);
149 }
150
151 static int ConnectPixelCacheServer(const char *hostname,const int port,
152   MagickSizeType *session_key,ExceptionInfo *exception)
153 {
154 #if defined(MAGICKCORE_HAVE_SOCKET)
155   char
156     secret[MaxTextExtent];
157
158   const char
159     *shared_secret;
160
161   int
162     client_socket,
163     status;
164
165   ssize_t
166     count;
167
168   struct hostent
169     *host;
170
171   struct sockaddr_in
172     address;
173
174   unsigned char
175     session[MaxTextExtent];
176
177   /*
178     Connect to distributed pixel cache and get session key.
179   */
180   *session_key=0;
181   shared_secret=GetPolicyValue("shared-secret");
182   if (shared_secret == (const char *) NULL)
183     {
184       (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
185         "DistributedPixelCache","'%s'","shared secret expected");
186       return(-1);
187     }
188   (void) CopyMagickString((char *) session,shared_secret,MaxTextExtent-
189     DPCSessionKeyLength);
190   host=gethostbyname(hostname);
191   client_socket=socket(AF_INET,SOCK_STREAM,0);
192   if (client_socket == -1)
193     {
194       (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
195         "DistributedPixelCache","'%s'",hostname);
196       return(-1);
197     }
198   (void) ResetMagickMemory(&address,0,sizeof(address));
199   address.sin_family=AF_INET;
200   address.sin_port=htons((uint16_t) port);
201   address.sin_addr=(*((struct in_addr *) host->h_addr));
202   status=connect(client_socket,(struct sockaddr *) &address,(socklen_t)
203     sizeof(struct sockaddr));
204   if (status == -1)
205     {
206       (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
207         "DistributedPixelCache","'%s'",hostname);
208       return(-1);
209     }
210   count=read(client_socket,secret,MaxTextExtent);
211   if (count != -1)
212     {
213       (void) memcpy(session+strlen(shared_secret),secret,(size_t) count);
214       *session_key=CRC64(session,strlen(shared_secret)+count);
215     }
216   if (*session_key == 0)
217     {
218       close(client_socket);
219       client_socket=(-1);
220     }
221   return(client_socket);
222 #else
223   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
224     "DelegateLibrarySupportNotBuiltIn","distributed pixel cache");
225   return(MagickFalse);
226 #endif
227 }
228
229 static char *GetHostname(int *port,ExceptionInfo *exception)
230 {
231   char
232     *host,
233     *hosts,
234     **hostlist;
235
236   int
237     argc;
238
239   register ssize_t
240     i;
241
242   static size_t
243     id = 0;
244
245   /*
246     Parse host list (e.g. 192.168.100.1:6668,192.168.100.2:6668).
247   */
248   hosts=(char *) GetImageRegistry(StringRegistryType,"cache:hosts",
249     exception);
250   if (hosts == (char *) NULL)
251     {
252       *port=DPCPort;
253       return(AcquireString(DPCHostname));
254     }
255   (void) SubstituteString(&hosts,","," ");
256   hostlist=StringToArgv(hosts,&argc);
257   hosts=DestroyString(hosts);
258   if (hostlist == (char **) NULL)
259     {
260       *port=DPCPort;
261       return(AcquireString(DPCHostname));
262     }
263   hosts=AcquireString(hostlist[(id++ % (argc-1))+1]);
264   for (i=0; i < (ssize_t) argc; i++)
265     hostlist[i]=DestroyString(hostlist[i]);
266   hostlist=(char **) RelinquishMagickMemory(hostlist);
267   (void) SubstituteString(&hosts,":"," ");
268   hostlist=StringToArgv(hosts,&argc);
269   if (hostlist == (char **) NULL)
270     {
271       *port=DPCPort;
272       return(AcquireString(DPCHostname));
273     }
274   host=AcquireString(hostlist[1]);
275   if (hostlist[2] == (char *) NULL)
276     *port=DPCPort;
277   else
278     *port=StringToLong(hostlist[2]);
279   for (i=0; i < (ssize_t) argc; i++)
280     hostlist[i]=DestroyString(hostlist[i]);
281   hostlist=(char **) RelinquishMagickMemory(hostlist);
282   return(host);
283 }
284
285 MagickPrivate DistributeCacheInfo *AcquireDistributeCacheInfo(
286   ExceptionInfo *exception)
287 {
288   char
289     *hostname;
290
291   DistributeCacheInfo
292     *distribute_cache_info;
293
294   MagickSizeType
295     session_key;
296
297   distribute_cache_info=(DistributeCacheInfo *) AcquireMagickMemory(
298     sizeof(*distribute_cache_info));
299   if (distribute_cache_info == (DistributeCacheInfo *) NULL)
300     ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
301   (void) ResetMagickMemory(distribute_cache_info,0,
302     sizeof(*distribute_cache_info));
303   distribute_cache_info->signature=MagickSignature;
304   /*
305     Contact pixel cache server.
306   */
307   distribute_cache_info->port=0;
308   hostname=GetHostname(&distribute_cache_info->port,exception);
309   session_key=0;
310   distribute_cache_info->file=ConnectPixelCacheServer(hostname,
311     distribute_cache_info->port,&session_key,exception);
312   distribute_cache_info->session_key=session_key;
313   (void) CopyMagickString(distribute_cache_info->hostname,hostname,
314     MaxTextExtent);
315   hostname=DestroyString(hostname);
316   if (distribute_cache_info->file == -1)
317     distribute_cache_info=DestroyDistributeCacheInfo(distribute_cache_info);
318   return(distribute_cache_info);
319 }
320 \f
321 /*
322 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
323 %                                                                             %
324 %                                                                             %
325 %                                                                             %
326 +   D e s t r o y D i s t r i b u t e C a c h e I n f o                       %
327 %                                                                             %
328 %                                                                             %
329 %                                                                             %
330 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
331 %
332 %  DestroyDistributeCacheInfo() deallocates memory associated with an
333 %  DistributeCacheInfo structure.
334 %
335 %  The format of the DestroyDistributeCacheInfo method is:
336 %
337 %      DistributeCacheInfo *DestroyDistributeCacheInfo(
338 %        DistributeCacheInfo *distribute_cache_info)
339 %
340 %  A description of each parameter follows:
341 %
342 %    o distribute_cache_info: the distributed cache info.
343 %
344 */
345 MagickPrivate DistributeCacheInfo *DestroyDistributeCacheInfo(
346   DistributeCacheInfo *distribute_cache_info)
347 {
348   assert(distribute_cache_info != (DistributeCacheInfo *) NULL);
349   assert(distribute_cache_info->signature == MagickSignature);
350   distribute_cache_info->signature=(~MagickSignature);
351   distribute_cache_info=(DistributeCacheInfo *) RelinquishMagickMemory(
352     distribute_cache_info);
353   return(distribute_cache_info);
354 }
355 \f
356 /*
357 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
358 %                                                                             %
359 %                                                                             %
360 %                                                                             %
361 +   D i s t r i b u t e P i x e l C a c h e S e r v e r                       %
362 %                                                                             %
363 %                                                                             %
364 %                                                                             %
365 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
366 %
367 %  DistributePixelCacheServer() waits on the specified port for commands to
368 %  create, read, update, or destroy a pixel cache.
369 %
370 %  The format of the DistributePixelCacheServer() method is:
371 %
372 %      void DistributePixelCacheServer(const size_t port)
373 %
374 %  A description of each parameter follows:
375 %
376 %    o port: connect the distributed pixel cache at this port.
377 %
378 %    o exception: return any errors or warnings in this structure.
379 %
380 */
381
382 static MagickBooleanType CreateDistributeCache(SplayTreeInfo *image_registry,
383   int file,const MagickSizeType session_key)
384 {
385   ExceptionInfo
386     *exception;
387
388   Image
389     *image;
390
391   MagickBooleanType
392     status;
393
394   register unsigned char
395     *p;
396
397   size_t
398     length;
399
400   ssize_t
401     count;
402
403   unsigned char
404     buffer[MaxTextExtent];
405
406   exception=AcquireExceptionInfo();
407   image=AcquireImage((ImageInfo *) NULL,exception);
408   exception=DestroyExceptionInfo(exception);
409   length=sizeof(image->columns)+sizeof(image->rows)+
410     sizeof(image->number_channels);
411   count=read(file,buffer,length);
412   if (count != (ssize_t) length)
413     return(MagickFalse);
414   p=buffer;
415   (void) memcpy(&image->columns,p,sizeof(image->columns));
416   p+=sizeof(image->columns);
417   (void) memcpy(&image->rows,p,sizeof(image->rows));
418   p+=sizeof(image->rows);
419   (void) memcpy(&image->number_channels,p,sizeof(image->number_channels));
420   p+=sizeof(image->number_channels);
421   status=AddValueToSplayTree(image_registry,(const void *) session_key,image);
422   return(status);
423 }
424
425 static MagickBooleanType DestroyDistributeCache(SplayTreeInfo *image_registry,
426   int file,const MagickSizeType session_key)
427 {
428   return(DeleteNodeFromSplayTree(image_registry,(const void *) session_key));
429 }
430
431 static MagickBooleanType ReadDistributeCacheMetacontent(
432   SplayTreeInfo *image_registry,int file,const MagickSizeType session_key)
433 {
434   const unsigned char
435     *metacontent;
436
437   ExceptionInfo
438     *exception;
439
440   Image
441     *image;
442
443   RectangleInfo
444     region;
445
446   register const Quantum
447     *p;
448
449   register unsigned char
450     *q;
451
452   size_t
453     length;
454
455   ssize_t
456     count;
457
458   unsigned char
459     buffer[MaxTextExtent];
460
461   image=(Image *) GetValueFromSplayTree(image_registry,(const void *)
462     session_key);
463   if (image == (Image *) NULL)
464     return(MagickFalse);
465   length=sizeof(region.width)+sizeof(region.height)+sizeof(region.x)+
466     sizeof(region.y)+sizeof(length);
467   count=read(file,buffer,length);
468   if (count != (ssize_t) length)
469     return(MagickFalse);
470   q=buffer;
471   (void) memcpy(&region.width,q,sizeof(region.width));
472   q+=sizeof(region.width);
473   (void) memcpy(&region.height,q,sizeof(region.height));
474   q+=sizeof(region.width);
475   (void) memcpy(&region.x,q,sizeof(region.x));
476   q+=sizeof(region.width);
477   (void) memcpy(&region.y,q,sizeof(region.y));
478   q+=sizeof(region.width);
479   (void) memcpy(&length,q,sizeof(length));
480   q+=sizeof(length);
481   exception=AcquireExceptionInfo();
482   p=GetVirtualPixels(image,region.x,region.y,region.width,region.height,
483     exception);
484   exception=DestroyExceptionInfo(exception);
485   if (p == (const Quantum *) NULL)
486     return(MagickFalse);
487   metacontent=GetVirtualMetacontent(image);
488   count=write(file,metacontent,length);
489   if (count != (ssize_t) length)
490     return(MagickFalse);
491   return(MagickTrue);
492 }
493
494 static MagickBooleanType ReadDistributeCachePixels(
495   SplayTreeInfo *image_registry,int file,const MagickSizeType session_key)
496 {
497   ExceptionInfo
498     *exception;
499
500   Image
501     *image;
502
503   RectangleInfo
504     region;
505
506   register const Quantum
507     *p;
508
509   register unsigned char
510     *q;
511
512   size_t
513     length;
514
515   ssize_t
516     count;
517
518   unsigned char
519     buffer[MaxTextExtent];
520
521   image=(Image *) GetValueFromSplayTree(image_registry,(const void *)
522     session_key);
523   if (image == (Image *) NULL)
524     return(MagickFalse);
525   length=sizeof(region.width)+sizeof(region.height)+sizeof(region.x)+
526     sizeof(region.y)+sizeof(length);
527   count=read(file,buffer,length);
528   if (count != (ssize_t) length)
529     return(MagickFalse);
530   q=buffer;
531   (void) memcpy(&region.width,q,sizeof(region.width));
532   q+=sizeof(region.width);
533   (void) memcpy(&region.height,q,sizeof(region.height));
534   q+=sizeof(region.width);
535   (void) memcpy(&region.x,q,sizeof(region.x));
536   q+=sizeof(region.width);
537   (void) memcpy(&region.y,q,sizeof(region.y));
538   q+=sizeof(region.width);
539   (void) memcpy(&length,q,sizeof(length));
540   q+=sizeof(length);
541   exception=AcquireExceptionInfo();
542   p=GetVirtualPixels(image,region.x,region.y,region.width,region.height,
543     exception);
544   exception=DestroyExceptionInfo(exception);
545   if (p == (const Quantum *) NULL)
546     return(MagickFalse);
547   count=write(file,p,length);
548   if (count != (ssize_t) length)
549     return(MagickFalse);
550   return(MagickTrue);
551 }
552
553 static MagickBooleanType WriteDistributeCacheMetacontent(
554   SplayTreeInfo *image_registry,int file,const MagickSizeType session_key)
555 {
556   ExceptionInfo
557     *exception;
558
559   Image
560     *image;
561
562   MagickBooleanType
563     status;
564
565   RectangleInfo
566     region;
567
568   register Quantum
569     *q;
570
571   register unsigned char
572     *p;
573
574   size_t
575     length;
576
577   ssize_t
578     count;
579
580   unsigned char
581     buffer[MaxTextExtent],
582     *metacontent;
583
584   image=(Image *) GetValueFromSplayTree(image_registry,(const void *)
585     session_key);
586   if (image == (Image *) NULL)
587     return(MagickFalse);
588   length=sizeof(region.width)+sizeof(region.height)+sizeof(region.x)+
589     sizeof(region.y)+sizeof(length);
590   count=read(file,buffer,length);
591   if (count != (ssize_t) length)
592     return(MagickFalse);
593   p=buffer;
594   (void) memcpy(&region.width,p,sizeof(region.width));
595   p+=sizeof(region.width);
596   (void) memcpy(&region.height,p,sizeof(region.height));
597   p+=sizeof(region.width);
598   (void) memcpy(&region.x,p,sizeof(region.x));
599   p+=sizeof(region.width);
600   (void) memcpy(&region.y,p,sizeof(region.y));
601   p+=sizeof(region.width);
602   (void) memcpy(&length,p,sizeof(length));
603   p+=sizeof(length);
604   exception=AcquireExceptionInfo();
605   q=GetAuthenticPixels(image,region.x,region.y,region.width,region.height,
606     exception);
607   exception=DestroyExceptionInfo(exception);
608   if (q == (Quantum *) NULL)
609     return(MagickFalse);
610   metacontent=GetAuthenticMetacontent(image);
611   count=read(file,metacontent,length);
612   if (count != (ssize_t) length)
613     return(MagickFalse);
614   status=SyncAuthenticPixels(image,exception);
615   return(status);
616 }
617
618 static MagickBooleanType WriteDistributeCachePixels(
619   SplayTreeInfo *image_registry,int file,const MagickSizeType session_key)
620 {
621   ExceptionInfo
622     *exception;
623
624   Image
625     *image;
626
627   MagickBooleanType
628     status;
629
630   RectangleInfo
631     region;
632
633   register Quantum
634     *q;
635
636   register unsigned char
637     *p;
638
639   size_t
640     length;
641
642   ssize_t
643     count;
644
645   unsigned char
646     buffer[MaxTextExtent];
647
648   image=(Image *) GetValueFromSplayTree(image_registry,(const void *)
649     session_key);
650   if (image == (Image *) NULL)
651     return(MagickFalse);
652   length=sizeof(region.width)+sizeof(region.height)+sizeof(region.x)+
653     sizeof(region.y)+sizeof(length);
654   count=read(file,buffer,length);
655   if (count != (ssize_t) length)
656     return(MagickFalse);
657   p=buffer;
658   (void) memcpy(&region.width,p,sizeof(region.width));
659   p+=sizeof(region.width);
660   (void) memcpy(&region.height,p,sizeof(region.height));
661   p+=sizeof(region.width);
662   (void) memcpy(&region.x,p,sizeof(region.x));
663   p+=sizeof(region.width);
664   (void) memcpy(&region.y,p,sizeof(region.y));
665   p+=sizeof(region.width);
666   (void) memcpy(&length,p,sizeof(length));
667   p+=sizeof(length);
668   exception=AcquireExceptionInfo();
669   q=GetAuthenticPixels(image,region.x,region.y,region.width,region.height,
670     exception);
671   exception=DestroyExceptionInfo(exception);
672   if (q == (Quantum *) NULL)
673     return(MagickFalse);
674   count=read(file,q,length);
675   if (count != (ssize_t) length)
676     return(MagickFalse);
677   status=SyncAuthenticPixels(image,exception);
678   return(status);
679 }
680
681 static void *DistributePixelCacheClient(void *socket)
682 {
683   const char
684     *shared_secret;
685
686   int
687     client_socket;
688
689   MagickBooleanType
690     status;
691
692   MagickSizeType
693     key,
694     session_key;
695
696   RandomInfo
697     *random_info;
698
699   SplayTreeInfo
700     *image_registry;
701
702   ssize_t
703     count;
704
705   StringInfo
706     *secret;
707
708   unsigned char
709     command,
710     session[MaxTextExtent];
711
712   /*
713     Generate session key.
714   */
715   shared_secret=GetPolicyValue("shared-secret");
716   if (shared_secret == (const char *) NULL)
717     ThrowFatalException(CacheFatalError,"shared secret expected");
718   (void) CopyMagickString((char *) session,shared_secret,MaxTextExtent-
719     DPCSessionKeyLength);
720   random_info=AcquireRandomInfo();
721   secret=GetRandomKey(random_info,DPCSessionKeyLength);
722   (void) memcpy(session+strlen(shared_secret),GetStringInfoDatum(secret),
723     DPCSessionKeyLength);
724   session_key=CRC64(session,strlen(shared_secret)+DPCSessionKeyLength);
725   random_info=DestroyRandomInfo(random_info);
726   image_registry=NewSplayTree((int (*)(const void *,const void *)) NULL,
727     (void *(*)(void *)) NULL,(void *(*)(void *)) NULL);
728   client_socket=(*(int *) socket);
729   count=write(client_socket,GetStringInfoDatum(secret),DPCSessionKeyLength);
730   secret=DestroyStringInfo(secret);
731   for ( ; ; )
732   {
733     count=read(client_socket,&command,1);
734     if (count <= 0)
735       break;
736     count=read(client_socket,&key,sizeof(key));
737     if ((count != (ssize_t) sizeof(key)) && (key != session_key))
738       break;
739     status=MagickFalse;
740     switch (command)
741     {
742       case 'c':
743       {
744         status=CreateDistributeCache(image_registry,client_socket,session_key);
745         break;
746       }
747       case 'r':
748       {
749         status=ReadDistributeCachePixels(image_registry,client_socket,
750           session_key);
751         break;
752       }
753       case 'u':
754       {
755         status=WriteDistributeCachePixels(image_registry,client_socket,
756           session_key);
757         break;
758       }
759       case 'd':
760       {
761         status=DestroyDistributeCache(image_registry,client_socket,session_key);
762         break;
763       }
764       case 'm':
765       {
766         status=ReadDistributeCacheMetacontent(image_registry,client_socket,
767           session_key);
768         break;
769       }
770       case 'M':
771       {
772         status=WriteDistributeCacheMetacontent(image_registry,client_socket,
773           session_key);
774         break;
775       }
776       default:
777         break;
778     }
779     count=write(client_socket,&status,sizeof(status));
780     if (count != (ssize_t) sizeof(status))
781       break;
782   }
783   return((void *) NULL);
784 }
785
786 MagickExport void DistributePixelCacheServer(const size_t port,
787   ExceptionInfo *exception)
788 {
789 #if defined(MAGICKCORE_HAVE_SOCKET) && defined(MAGICKCORE_THREAD_SUPPORT)
790   int
791     server_socket,
792     status;
793
794   pthread_attr_t
795     attributes;
796
797   pthread_t
798     threads;
799
800   struct sockaddr_in
801     address;
802
803   /*
804     Launch distributed pixel cache server.
805   */
806   server_socket=socket(AF_INET,SOCK_STREAM,0);
807   address.sin_family=AF_INET;
808   address.sin_port=htons(port);
809   address.sin_addr.s_addr=htonl(INADDR_ANY);
810   status=bind(server_socket,(struct sockaddr *) &address,(socklen_t)
811     sizeof(address));
812   if (status != 0)
813     ThrowFatalException(CacheFatalError,"UnableToBind");
814   status=listen(server_socket,32);
815   if (status != 0)
816     ThrowFatalException(CacheFatalError,"UnableToListen");
817   pthread_attr_init(&attributes);
818   for ( ; ; )
819   {
820     int
821       client_socket;
822
823     socklen_t
824       length;
825
826     length=(socklen_t) sizeof(address);
827     client_socket=accept(server_socket,(struct sockaddr *) &address,&length);
828     if (client_socket == -1)
829       ThrowFatalException(CacheFatalError,"UnableToEstablishConnection");
830     status=pthread_create(&threads,&attributes,DistributePixelCacheClient,
831       (void *) &client_socket);
832     if (status == -1)
833       ThrowFatalException(CacheFatalError,"UnableToCreateClientThread");
834   }
835   (void) close(server_socket);
836 #else
837   ThrowFatalException(MissingDelegateError,"distributed pixel cache");
838 #endif
839 }
840 \f
841 /*
842 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
843 %                                                                             %
844 %                                                                             %
845 %                                                                             %
846 +   G e t D i s t r i b u t e C a c h e F i l e                               %
847 %                                                                             %
848 %                                                                             %
849 %                                                                             %
850 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
851 %
852 %  GetDistributeCacheFile() returns the file associated with this
853 %  DistributeCacheInfo structure.
854 %
855 %  The format of the GetDistributeCacheFile method is:
856 %
857 %      int GetDistributeCacheFile(
858 %        const DistributeCacheInfo *distribute_cache_info)
859 %
860 %  A description of each parameter follows:
861 %
862 %    o distribute_cache_info: the distributed cache info.
863 %
864 */
865 MagickPrivate int GetDistributeCacheFile(
866   const DistributeCacheInfo *distribute_cache_info)
867 {
868   assert(distribute_cache_info != (DistributeCacheInfo *) NULL);
869   assert(distribute_cache_info->signature == MagickSignature);
870   return(distribute_cache_info->file);
871 }
872 \f
873 /*
874 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
875 %                                                                             %
876 %                                                                             %
877 %                                                                             %
878 +   G e t D i s t r i b u t e C a c h e H o s t n a m e                       %
879 %                                                                             %
880 %                                                                             %
881 %                                                                             %
882 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
883 %
884 %  GetDistributeCacheHostname() returns the hostname associated with this
885 %  DistributeCacheInfo structure.
886 %
887 %  The format of the GetDistributeCacheHostname method is:
888 %
889 %      const char *GetDistributeCacheHostname(
890 %        const DistributeCacheInfo *distribute_cache_info)
891 %
892 %  A description of each parameter follows:
893 %
894 %    o distribute_cache_info: the distributed cache info.
895 %
896 */
897 MagickPrivate const char *GetDistributeCacheHostname(
898   const DistributeCacheInfo *distribute_cache_info)
899 {
900   assert(distribute_cache_info != (DistributeCacheInfo *) NULL);
901   assert(distribute_cache_info->signature == MagickSignature);
902   return(distribute_cache_info->hostname);
903 }
904 \f
905 /*
906 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
907 %                                                                             %
908 %                                                                             %
909 %                                                                             %
910 +   G e t D i s t r i b u t e C a c h e P o r t                               %
911 %                                                                             %
912 %                                                                             %
913 %                                                                             %
914 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
915 %
916 %  GetDistributeCachePort() returns the port associated with this
917 %  DistributeCacheInfo structure.
918 %
919 %  The format of the GetDistributeCachePort method is:
920 %
921 %      int GetDistributeCachePort(
922 %        const DistributeCacheInfo *distribute_cache_info)
923 %
924 %  A description of each parameter follows:
925 %
926 %    o distribute_cache_info: the distributed cache info.
927 %
928 */
929 MagickPrivate int GetDistributeCachePort(
930   const DistributeCacheInfo *distribute_cache_info)
931 {
932   assert(distribute_cache_info != (DistributeCacheInfo *) NULL);
933   assert(distribute_cache_info->signature == MagickSignature);
934   return(distribute_cache_info->port);
935 }
936 \f
937 /*
938 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
939 %                                                                             %
940 %                                                                             %
941 %                                                                             %
942 +   O p e n D i s t r i b u t e P i x e l C a c h e                           %
943 %                                                                             %
944 %                                                                             %
945 %                                                                             %
946 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
947 %
948 %  OpenDistributePixelCache() opens a pixel cache on a remote server.
949 %
950 %  The format of the OpenDistributePixelCache method is:
951 %
952 %      MagickBooleanType *OpenDistributePixelCache(
953 %        DistributeCacheInfo *distribute_cache_info,Image *image)
954 %
955 %  A description of each parameter follows:
956 %
957 %    o distribute_cache_info: the distributed cache info.
958 %
959 %    o image: the image.
960 %
961 */
962 MagickPrivate MagickBooleanType OpenDistributePixelCache(
963   DistributeCacheInfo *distribute_cache_info,Image *image)
964 {
965   MagickBooleanType
966     status;
967
968   register unsigned char
969     *p;
970
971   ssize_t
972     count;
973
974   unsigned char
975     buffer[MaxTextExtent];
976
977   assert(distribute_cache_info != (DistributeCacheInfo *) NULL);
978   assert(distribute_cache_info->signature == MagickSignature);
979   assert(image != (Image *) NULL);
980   assert(image->signature == MagickSignature);
981   p=buffer;
982   *p++='c';  /* create */
983   (void) memcpy(p,&distribute_cache_info->session_key,
984     sizeof(distribute_cache_info->session_key));
985   p+=sizeof(distribute_cache_info->session_key);
986   (void) memcpy(p,&image->columns,sizeof(image->columns));
987   p+=sizeof(image->columns);
988   (void) memcpy(p,&image->rows,sizeof(image->rows));
989   p+=sizeof(image->rows);
990   (void) memcpy(p,&image->number_channels,sizeof(image->number_channels));
991   p+=sizeof(image->number_channels);
992   count=write(distribute_cache_info->file,buffer,p-buffer);
993   if (count != (ssize_t) (p-buffer))
994     return(MagickFalse);
995   count=read(distribute_cache_info->file,&status,sizeof(status));
996   if (count != (ssize_t) sizeof(status))
997     return(MagickFalse);
998   return(MagickTrue);
999 }
1000 \f
1001 /*
1002 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1003 %                                                                             %
1004 %                                                                             %
1005 %                                                                             %
1006 +   R e a d D i s t r i b u t e P i x e l C a c h e M e t a c o n t e n t     %
1007 %                                                                             %
1008 %                                                                             %
1009 %                                                                             %
1010 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1011 %
1012 %  ReadDistributePixelCacheMetacontents() reads metacontent from the specified
1013 %  region of the distributed pixel cache.
1014 %
1015 %  The format of the ReadDistributePixelCacheMetacontents method is:
1016 %
1017 %      MagickBooleanType *ReadDistributePixelCacheMetacontents(
1018 %        DistributeCacheInfo *distribute_cache_info,const RectangleInfo *region,
1019 %        const MagickSizeType length,unsigned char *metacontent)
1020 %
1021 %  A description of each parameter follows:
1022 %
1023 %    o distribute_cache_info: the distributed cache info.
1024 %
1025 %    o image: the image.
1026 %
1027 %    o region: read the metacontent from this region of the image.
1028 %
1029 %    o length: the length in bytes of the metacontent.
1030 %
1031 %    o metacontent: read these metacontent from the pixel cache.
1032 %
1033 */
1034 MagickPrivate MagickBooleanType ReadDistributePixelCacheMetacontent(
1035   DistributeCacheInfo *distribute_cache_info,const RectangleInfo *region,
1036   const MagickSizeType length,unsigned char *metacontent)
1037 {
1038   MagickBooleanType
1039     status;
1040
1041   register unsigned char
1042     *p;
1043
1044   ssize_t
1045     count;
1046
1047   unsigned char
1048     buffer[MaxTextExtent];
1049
1050   assert(distribute_cache_info != (DistributeCacheInfo *) NULL);
1051   assert(distribute_cache_info->signature == MagickSignature);
1052   assert(region != (RectangleInfo *) NULL);
1053   assert(metacontent != (unsigned char *) NULL);
1054   assert(length == ((size_t) length));
1055   p=buffer;
1056   *p++='m';  /* read */
1057   (void) memcpy(p,&distribute_cache_info->session_key,
1058     sizeof(distribute_cache_info->session_key));
1059   p+=sizeof(distribute_cache_info->session_key);
1060   (void) memcpy(p,&region->width,sizeof(region->width));
1061   p+=sizeof(region->width);
1062   (void) memcpy(p,&region->height,sizeof(region->height));
1063   p+=sizeof(region->height);
1064   (void) memcpy(p,&region->x,sizeof(region->x));
1065   p+=sizeof(region->x);
1066   (void) memcpy(p,&region->y,sizeof(region->y));
1067   p+=sizeof(region->y);
1068   (void) memcpy(p,&length,sizeof(length));
1069   p+=sizeof(length);
1070   count=write(distribute_cache_info->file,buffer,p-buffer);
1071   if (count != (ssize_t) (p-buffer))
1072     return(MagickFalse);
1073   count=read(distribute_cache_info->file,(unsigned char *) metacontent,(size_t)
1074     length);
1075   if (count != (ssize_t) length)
1076     return(MagickFalse);
1077   count=read(distribute_cache_info->file,&status,sizeof(status));
1078   if (count != (ssize_t) sizeof(status))
1079     return(MagickFalse);
1080   return(status != 0 ? MagickTrue : MagickFalse);
1081 }
1082 \f
1083 /*
1084 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1085 %                                                                             %
1086 %                                                                             %
1087 %                                                                             %
1088 +   R e a d D i s t r i b u t e P i x e l C a c h e P i x e l s               %
1089 %                                                                             %
1090 %                                                                             %
1091 %                                                                             %
1092 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1093 %
1094 %  ReadDistributePixelCachePixels() reads pixels from the specified region of
1095 %  the distributed pixel cache.
1096 %
1097 %  The format of the ReadDistributePixelCachePixels method is:
1098 %
1099 %      MagickBooleanType *ReadDistributePixelCachePixels(
1100 %        DistributeCacheInfo *distribute_cache_info,const RectangleInfo *region,
1101 %        const MagickSizeType length,unsigned char *pixels)
1102 %
1103 %  A description of each parameter follows:
1104 %
1105 %    o distribute_cache_info: the distributed cache info.
1106 %
1107 %    o image: the image.
1108 %
1109 %    o region: read the pixels from this region of the image.
1110 %
1111 %    o length: the length in bytes of the pixels.
1112 %
1113 %    o pixels: read these pixels from the pixel cache.
1114 %
1115 */
1116 MagickPrivate MagickBooleanType ReadDistributePixelCachePixels(
1117   DistributeCacheInfo *distribute_cache_info,const RectangleInfo *region,
1118   const MagickSizeType length,unsigned char *pixels)
1119 {
1120   MagickBooleanType
1121     status;
1122
1123   register unsigned char
1124     *p;
1125
1126   ssize_t
1127     count;
1128
1129   unsigned char
1130     buffer[MaxTextExtent];
1131
1132   assert(distribute_cache_info != (DistributeCacheInfo *) NULL);
1133   assert(distribute_cache_info->signature == MagickSignature);
1134   assert(region != (RectangleInfo *) NULL);
1135   assert(pixels != (unsigned char *) NULL);
1136   assert(length == ((size_t) length));
1137   p=buffer;
1138   *p++='r';  /* read */
1139   (void) memcpy(p,&distribute_cache_info->session_key,
1140     sizeof(distribute_cache_info->session_key));
1141   p+=sizeof(distribute_cache_info->session_key);
1142   (void) memcpy(p,&region->width,sizeof(region->width));
1143   p+=sizeof(region->width);
1144   (void) memcpy(p,&region->height,sizeof(region->height));
1145   p+=sizeof(region->height);
1146   (void) memcpy(p,&region->x,sizeof(region->x));
1147   p+=sizeof(region->x);
1148   (void) memcpy(p,&region->y,sizeof(region->y));
1149   p+=sizeof(region->y);
1150   (void) memcpy(p,&length,sizeof(length));
1151   p+=sizeof(length);
1152   count=write(distribute_cache_info->file,buffer,p-buffer);
1153   if (count != (ssize_t) (p-buffer))
1154     return(MagickFalse);
1155   count=read(distribute_cache_info->file,(unsigned char *) pixels,(size_t)
1156     length);
1157   if (count != (ssize_t) length)
1158     return(MagickFalse);
1159   count=read(distribute_cache_info->file,&status,sizeof(status));
1160   if (count != (ssize_t) sizeof(status))
1161     return(MagickFalse);
1162   return(status != 0 ? MagickTrue : MagickFalse);
1163 }
1164 \f
1165 /*
1166 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1167 %                                                                             %
1168 %                                                                             %
1169 %                                                                             %
1170 +   R e l i n q u i s h D i s t r i b u t e P i x e l C a c h e               %
1171 %                                                                             %
1172 %                                                                             %
1173 %                                                                             %
1174 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1175 %
1176 %  RelinquishDistributePixelCache() frees resources acquired with
1177 %  OpenDistributePixelCache().
1178 %
1179 %  The format of the RelinquishDistributePixelCache method is:
1180 %
1181 %      MagickBooleanType RelinquishDistributePixelCache(
1182 %        DistributeCacheInfo *distribute_cache_info)
1183 %
1184 %  A description of each parameter follows:
1185 %
1186 %    o distribute_cache_info: the distributed cache info.
1187 %
1188 */
1189 MagickPrivate MagickBooleanType RelinquishDistributePixelCache(
1190   DistributeCacheInfo *distribute_cache_info)
1191 {
1192   register unsigned char
1193     *p;
1194
1195   ssize_t
1196     count;
1197
1198   unsigned char
1199     buffer[MaxTextExtent];
1200
1201   assert(distribute_cache_info != (DistributeCacheInfo *) NULL);
1202   assert(distribute_cache_info->signature == MagickSignature);
1203   p=buffer;
1204   *p++='d';  /* delete */
1205   (void) memcpy(p,&distribute_cache_info->session_key,
1206     sizeof(distribute_cache_info->session_key));
1207   p+=sizeof(distribute_cache_info->session_key);
1208   count=write(distribute_cache_info->file,buffer,p-buffer);
1209   if (count != (ssize_t) (p-buffer))
1210     return(MagickFalse);
1211   return(MagickTrue);
1212 }
1213 \f
1214 /*
1215 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1216 %                                                                             %
1217 %                                                                             %
1218 %                                                                             %
1219 +   W r i t e D i s t r i b u t e P i x e l C a c h e M e t a c o n t e n t   %
1220 %                                                                             %
1221 %                                                                             %
1222 %                                                                             %
1223 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1224 %
1225 %  WriteDistributePixelCacheMetacontents() writes image metacontent to the
1226 %  specified region of the distributed pixel cache.
1227 %
1228 %
1229 %  The format of the WriteDistributePixelCacheMetacontents method is:
1230 %
1231 %      MagickBooleanType *WriteDistributePixelCacheMetacontents(
1232 %        DistributeCacheInfo *distribute_cache_info,const RectangleInfo *region,
1233 %        const MagickSizeType length,const unsigned char *metacontent)
1234 %
1235 %  A description of each parameter follows:
1236 %
1237 %    o distribute_cache_info: the distributed cache info.
1238 %
1239 %    o image: the image.
1240 %
1241 %    o region: write the metacontent to this region of the image.
1242 %
1243 %    o length: the length in bytes of the metacontent.
1244 %
1245 %    o metacontent: write these metacontent to the pixel cache.
1246 %
1247 */
1248 MagickPrivate MagickBooleanType WriteDistributePixelCacheMetacontent(
1249   DistributeCacheInfo *distribute_cache_info,const RectangleInfo *region,
1250   const MagickSizeType length,const unsigned char *metacontent)
1251 {
1252   MagickBooleanType
1253     status;
1254
1255   register unsigned char
1256     *p;
1257
1258   ssize_t
1259     count;
1260
1261   unsigned char
1262     buffer[MaxTextExtent];
1263
1264   assert(distribute_cache_info != (DistributeCacheInfo *) NULL);
1265   assert(distribute_cache_info->signature == MagickSignature);
1266   assert(region != (RectangleInfo *) NULL);
1267   assert(metacontent != (unsigned char *) NULL);
1268   assert(length == ((size_t) length));
1269   p=buffer;
1270   *p++='M';  /* update */
1271   (void) memcpy(p,&distribute_cache_info->session_key,
1272     sizeof(distribute_cache_info->session_key));
1273   p+=sizeof(distribute_cache_info->session_key);
1274   (void) memcpy(p,&region->width,sizeof(region->width));
1275   p+=sizeof(region->width);
1276   (void) memcpy(p,&region->height,sizeof(region->height));
1277   p+=sizeof(region->height);
1278   (void) memcpy(p,&region->x,sizeof(region->x));
1279   p+=sizeof(region->x);
1280   (void) memcpy(p,&region->y,sizeof(region->y));
1281   p+=sizeof(region->y);
1282   (void) memcpy(p,&length,sizeof(length));
1283   p+=sizeof(length);
1284   count=write(distribute_cache_info->file,buffer,p-buffer);
1285   if (count != (ssize_t) (p-buffer))
1286     return(MagickFalse);
1287   count=write(distribute_cache_info->file,(unsigned char *) metacontent,(size_t)
1288     length);
1289   if (count != (ssize_t) length)
1290     return(MagickFalse);
1291   count=read(distribute_cache_info->file,&status,sizeof(status));
1292   if (count != (ssize_t) sizeof(status))
1293     return(MagickFalse);
1294   return(status != 0 ? MagickTrue : MagickFalse);
1295 }
1296 \f
1297 /*
1298 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1299 %                                                                             %
1300 %                                                                             %
1301 %                                                                             %
1302 +   W r i t e D i s t r i b u t e P i x e l C a c h e P i x e l s             %
1303 %                                                                             %
1304 %                                                                             %
1305 %                                                                             %
1306 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1307 %
1308 %  WriteDistributePixelCachePixels() writes image pixels to the specified
1309 %  region of the distributed pixel cache.
1310 %
1311 %
1312 %  The format of the WriteDistributePixelCachePixels method is:
1313 %
1314 %      MagickBooleanType *WriteDistributePixelCachePixels(
1315 %        DistributeCacheInfo *distribute_cache_info,const RectangleInfo *region,
1316 %        const MagickSizeType length,const unsigned char *pixels)
1317 %
1318 %  A description of each parameter follows:
1319 %
1320 %    o distribute_cache_info: the distributed cache info.
1321 %
1322 %    o image: the image.
1323 %
1324 %    o region: write the pixels to this region of the image.
1325 %
1326 %    o length: the length in bytes of the pixels.
1327 %
1328 %    o pixels: write these pixels to the pixel cache.
1329 %
1330 */
1331 MagickPrivate MagickBooleanType WriteDistributePixelCachePixels(
1332   DistributeCacheInfo *distribute_cache_info,const RectangleInfo *region,
1333   const MagickSizeType length,const unsigned char *pixels)
1334 {
1335   MagickBooleanType
1336     status;
1337
1338   register unsigned char
1339     *p;
1340
1341   ssize_t
1342     count;
1343
1344   unsigned char
1345     buffer[MaxTextExtent];
1346
1347   assert(distribute_cache_info != (DistributeCacheInfo *) NULL);
1348   assert(distribute_cache_info->signature == MagickSignature);
1349   assert(region != (RectangleInfo *) NULL);
1350   assert(pixels != (const unsigned char *) NULL);
1351   assert(length == ((size_t) length));
1352   p=buffer;
1353   *p++='u';  /* update */
1354   (void) memcpy(p,&distribute_cache_info->session_key,
1355     sizeof(distribute_cache_info->session_key));
1356   p+=sizeof(distribute_cache_info->session_key);
1357   (void) memcpy(p,&region->width,sizeof(region->width));
1358   p+=sizeof(region->width);
1359   (void) memcpy(p,&region->height,sizeof(region->height));
1360   p+=sizeof(region->height);
1361   (void) memcpy(p,&region->x,sizeof(region->x));
1362   p+=sizeof(region->x);
1363   (void) memcpy(p,&region->y,sizeof(region->y));
1364   p+=sizeof(region->y);
1365   (void) memcpy(p,&length,sizeof(length));
1366   p+=sizeof(length);
1367   count=write(distribute_cache_info->file,buffer,p-buffer);
1368   if (count != (ssize_t) (p-buffer))
1369     return(MagickFalse);
1370   count=write(distribute_cache_info->file,(unsigned char *) pixels,(size_t)
1371     length);
1372   if (count != (ssize_t) length)
1373     return(MagickFalse);
1374   count=read(distribute_cache_info->file,&status,sizeof(status));
1375   if (count != (ssize_t) sizeof(status))
1376     return(MagickFalse);
1377   return(status != 0 ? MagickTrue : MagickFalse);
1378 }