]> 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/list.h"
64 #include "MagickCore/locale_.h"
65 #include "MagickCore/memory_.h"
66 #include "MagickCore/pixel.h"
67 #include "MagickCore/policy.h"
68 #include "MagickCore/random_.h"
69 #include "MagickCore/registry.h"
70 #include "MagickCore/splay-tree.h"
71 #include "MagickCore/string_.h"
72 #include "MagickCore/string-private.h"
73 #include "MagickCore/version.h"
74 #if defined(MAGICKCORE_HAVE_SOCKET)
75 #include <netinet/in.h>
76 #include <netdb.h>
77 #include <sys/socket.h>
78 #include <arpa/inet.h>
79 #endif
80 \f
81 /*
82   Define declarations.
83 */
84 #define DPCHostname  "127.0.0.1"
85 #define DPCPendingConnections  10
86 #define DPCPort  6668
87 #define DPCSessionKeyLength  8
88 #ifndef MSG_NOSIGNAL
89 #  define MSG_NOSIGNAL 0
90 #endif
91 \f
92 /*
93 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
94 %                                                                             %
95 %                                                                             %
96 %                                                                             %
97 +   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                       %
98 %                                                                             %
99 %                                                                             %
100 %                                                                             %
101 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
102 %
103 %  AcquireDistributeCacheInfo() allocates the DistributeCacheInfo structure.
104 %
105 %  The format of the AcquireDistributeCacheInfo method is:
106 %
107 %      DistributeCacheInfo *AcquireDistributeCacheInfo(ExceptionInfo *exception)
108 %
109 %  A description of each parameter follows:
110 %
111 %    o exception: return any errors or warnings in this structure.
112 %
113 */
114
115 static size_t CRC64(const unsigned char *message,const MagickSizeType length)
116 {
117   register MagickOffsetType
118     i;
119
120   size_t
121     crc;
122
123   static MagickBooleanType
124     crc_initial = MagickFalse;
125
126   static size_t
127     crc_xor[256];
128
129   /*
130     Generate a 64-bit cyclic redundancy check for the message.
131   */
132   if (crc_initial == MagickFalse)
133     {
134       size_t
135         alpha;
136
137       for (i=0; i < 256; i++)
138       {
139         register MagickOffsetType
140           j;
141
142         alpha=(size_t) i;
143         for (j=0; j < 8; j++)
144         {
145           if ((alpha & 0x01) == 0)
146             alpha>>=1;
147           else
148             alpha=(size_t) ((alpha >> 1) ^
149               MagickULLConstant(0xd800000000000000));
150         }
151         crc_xor[i]=alpha;
152       }
153       crc_initial=MagickTrue;
154     }
155   crc=0;
156   for (i=0; i < (MagickOffsetType) length; i++)
157     crc=crc_xor[(crc ^ message[i]) & 0xff] ^ (crc >> 8);
158   return(crc);
159 }
160
161 static inline MagickSizeType MagickMin(const MagickSizeType x,
162   const MagickSizeType y)
163 {
164   if (x < y)
165     return(x);
166   return(y);
167 }
168
169 static inline MagickOffsetType dpc_read(int file,const MagickSizeType length,
170   unsigned char *restrict message)
171 {
172   register MagickOffsetType
173     i;
174
175   ssize_t
176     count;
177
178   count=0;
179   for (i=0; i < (MagickOffsetType) length; i+=count)
180   {
181     count=recv(file,message+i,(size_t) MagickMin(length-i,(MagickSizeType)
182       SSIZE_MAX),0);
183     if (count <= 0)
184       {
185         count=0;
186         if (errno != EINTR)
187           break;
188       }
189   }
190   return(i);
191 }
192
193 static int ConnectPixelCacheServer(const char *hostname,const int port,
194   size_t *session_key,ExceptionInfo *exception)
195 {
196 #if defined(MAGICKCORE_HAVE_SOCKET)
197   char
198     service[MaxTextExtent];
199
200   const char
201     *shared_secret;
202
203   int
204     client_socket,
205     status;
206
207   register unsigned char
208     *p;
209
210   ssize_t
211     count;
212
213   struct addrinfo
214     hint,
215     *result;
216
217   unsigned char
218     secret[MaxTextExtent],
219     session[2*MaxTextExtent];
220
221   /*
222     Connect to distributed pixel cache and get session key.
223   */
224   *session_key=0;
225   shared_secret=GetPolicyValue("shared-secret");
226   if (shared_secret == (const char *) NULL)
227     {
228       (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
229         "DistributedPixelCache","'%s'","shared secret expected");
230       return(-1);
231     }
232   p=session;
233   (void) CopyMagickString((char *) p,shared_secret,MaxTextExtent);
234   p+=strlen(shared_secret);
235   (void) ResetMagickMemory(&hint,0,sizeof(hint));
236   hint.ai_family=AF_INET;
237   hint.ai_socktype=SOCK_STREAM;
238   hint.ai_flags=AI_PASSIVE;
239   (void) FormatLocaleString(service,MaxTextExtent,"%d",port);
240   status=getaddrinfo(hostname,service,&hint,&result);
241   if (status != 0)
242     {
243       (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
244         "DistributedPixelCache","'%s'",hostname);
245       return(-1);
246     }
247   client_socket=socket(result->ai_family,result->ai_socktype,
248     result->ai_protocol);
249   if (client_socket == -1)
250     {
251       (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
252         "DistributedPixelCache","'%s'",hostname);
253       return(-1);
254     }
255   status=connect(client_socket,result->ai_addr,result->ai_addrlen);
256   if (status == -1)
257     {
258       (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
259         "DistributedPixelCache","'%s'",hostname);
260       return(-1);
261     }
262   count=recv(client_socket,secret,MaxTextExtent,0);
263   if (count != -1)
264     {
265       size_t
266         signature;
267
268       (void) memcpy(p,secret,(size_t) count);
269       p+=count;
270       signature=MagickLibVersion;
271       (void) memcpy(p,&signature,sizeof(signature));
272       p+=sizeof(signature);
273       signature=MAGICKCORE_QUANTUM_DEPTH;
274       (void) memcpy(p,&signature,sizeof(signature));
275       p+=sizeof(signature);
276       signature=MAGICKCORE_HDRI_ENABLE;
277       (void) memcpy(p,&signature,sizeof(signature));
278       p+=sizeof(signature);
279       *session_key=CRC64(session,p-session);
280     }
281   if (*session_key == 0)
282     {
283       close(client_socket);
284       client_socket=(-1);
285     }
286   return(client_socket);
287 #else
288   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
289     "DelegateLibrarySupportNotBuiltIn","distributed pixel cache");
290   return(MagickFalse);
291 #endif
292 }
293
294 static char *GetHostname(int *port,ExceptionInfo *exception)
295 {
296   char
297     *host,
298     *hosts,
299     **hostlist;
300
301   int
302     argc;
303
304   register ssize_t
305     i;
306
307   static size_t
308     id = 0;
309
310   /*
311     Parse host list (e.g. 192.168.100.1:6668,192.168.100.2:6668).
312   */
313   hosts=(char *) GetImageRegistry(StringRegistryType,"cache:hosts",
314     exception);
315   if (hosts == (char *) NULL)
316     {
317       *port=DPCPort;
318       return(AcquireString(DPCHostname));
319     }
320   (void) SubstituteString(&hosts,","," ");
321   hostlist=StringToArgv(hosts,&argc);
322   hosts=DestroyString(hosts);
323   if (hostlist == (char **) NULL)
324     {
325       *port=DPCPort;
326       return(AcquireString(DPCHostname));
327     }
328   hosts=AcquireString(hostlist[(id++ % (argc-1))+1]);
329   for (i=0; i < (ssize_t) argc; i++)
330     hostlist[i]=DestroyString(hostlist[i]);
331   hostlist=(char **) RelinquishMagickMemory(hostlist);
332   (void) SubstituteString(&hosts,":"," ");
333   hostlist=StringToArgv(hosts,&argc);
334   if (hostlist == (char **) NULL)
335     {
336       *port=DPCPort;
337       return(AcquireString(DPCHostname));
338     }
339   host=AcquireString(hostlist[1]);
340   if (hostlist[2] == (char *) NULL)
341     *port=DPCPort;
342   else
343     *port=StringToLong(hostlist[2]);
344   for (i=0; i < (ssize_t) argc; i++)
345     hostlist[i]=DestroyString(hostlist[i]);
346   hostlist=(char **) RelinquishMagickMemory(hostlist);
347   return(host);
348 }
349
350 MagickPrivate DistributeCacheInfo *AcquireDistributeCacheInfo(
351   ExceptionInfo *exception)
352 {
353   char
354     *hostname;
355
356   DistributeCacheInfo
357     *server_info;
358
359   size_t
360     session_key;
361
362   /*
363     Connect to the distributed pixel cache server.
364   */
365   server_info=(DistributeCacheInfo *) AcquireMagickMemory(sizeof(*server_info));
366   if (server_info == (DistributeCacheInfo *) NULL)
367     ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
368   (void) ResetMagickMemory(server_info,0,sizeof(*server_info));
369   server_info->signature=MagickSignature;
370   server_info->port=0;
371   hostname=GetHostname(&server_info->port,exception);
372   session_key=0;
373   server_info->file=ConnectPixelCacheServer(hostname,server_info->port,
374     &session_key,exception);
375   server_info->session_key=session_key;
376   (void) CopyMagickString(server_info->hostname,hostname,MaxTextExtent);
377   hostname=DestroyString(hostname);
378   if (server_info->file == -1)
379     server_info=DestroyDistributeCacheInfo(server_info);
380   return(server_info);
381 }
382 \f
383 /*
384 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
385 %                                                                             %
386 %                                                                             %
387 %                                                                             %
388 +   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                       %
389 %                                                                             %
390 %                                                                             %
391 %                                                                             %
392 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
393 %
394 %  DestroyDistributeCacheInfo() deallocates memory associated with an
395 %  DistributeCacheInfo structure.
396 %
397 %  The format of the DestroyDistributeCacheInfo method is:
398 %
399 %      DistributeCacheInfo *DestroyDistributeCacheInfo(
400 %        DistributeCacheInfo *server_info)
401 %
402 %  A description of each parameter follows:
403 %
404 %    o server_info: the distributed cache info.
405 %
406 */
407 MagickPrivate DistributeCacheInfo *DestroyDistributeCacheInfo(
408   DistributeCacheInfo *server_info)
409 {
410   assert(server_info != (DistributeCacheInfo *) NULL);
411   assert(server_info->signature == MagickSignature);
412   if (server_info->file > 0)
413     (void) close(server_info->file);
414   server_info->signature=(~MagickSignature);
415   server_info=(DistributeCacheInfo *) RelinquishMagickMemory(server_info);
416   return(server_info);
417 }
418 \f
419 /*
420 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
421 %                                                                             %
422 %                                                                             %
423 %                                                                             %
424 +   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                       %
425 %                                                                             %
426 %                                                                             %
427 %                                                                             %
428 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
429 %
430 %  DistributePixelCacheServer() waits on the specified port for commands to
431 %  create, read, update, or destroy a pixel cache.
432 %
433 %  The format of the DistributePixelCacheServer() method is:
434 %
435 %      void DistributePixelCacheServer(const int port)
436 %
437 %  A description of each parameter follows:
438 %
439 %    o port: connect the distributed pixel cache at this port.
440 %
441 %    o exception: return any errors or warnings in this structure.
442 %
443 */
444
445 static MagickBooleanType DestroyDistributeCache(SplayTreeInfo *registry,
446   int file,const size_t session_key)
447 {
448   /*
449     Destroy distributed pixel cache.
450   */
451   return(DeleteNodeFromSplayTree(registry,(const void *) session_key));
452 }
453
454 static inline MagickOffsetType dpc_send(int file,const MagickSizeType length,
455   const unsigned char *restrict message)
456 {
457   MagickOffsetType
458     count;
459
460   register MagickOffsetType
461     i;
462
463   /*
464     Ensure a complete message is sent.
465   */
466   count=0;
467   for (i=0; i < (MagickOffsetType) length; i+=count)
468   {
469     count=(MagickOffsetType) send(file,message+i,(size_t) MagickMin(length-i,
470       (MagickSizeType) SSIZE_MAX),MSG_NOSIGNAL);
471     if (count <= 0)
472       {
473         count=0;
474         if (errno != EINTR)
475           break;
476       }
477   }
478   return(i);
479 }
480
481 static MagickBooleanType OpenDistributeCache(SplayTreeInfo *registry,
482   int file,const size_t session_key,ExceptionInfo *exception)
483 {
484   Image
485     *image;
486
487   MagickBooleanType
488     status;
489
490   MagickOffsetType
491     count;
492
493   MagickSizeType
494     length;
495
496   register unsigned char
497     *p;
498
499   unsigned char
500     message[MaxTextExtent];
501
502   /*
503     Open distributed pixel cache.
504   */
505   image=AcquireImage((ImageInfo *) NULL,exception);
506   if (image == (Image *) NULL)
507     return(MagickFalse);
508   length=sizeof(image->storage_class)+sizeof(image->colorspace)+
509     sizeof(image->alpha_trait)+sizeof(image->mask)+sizeof(image->columns)+
510     sizeof(image->rows)+sizeof(image->number_channels)+MaxPixelChannels*
511     sizeof(*image->channel_map)+sizeof(image->metacontent_extent);
512   count=dpc_read(file,length,message);
513   if (count != (MagickOffsetType) length)
514     return(MagickFalse);
515   /*
516     Deserialize the image attributes.
517   */
518   p=message;
519   (void) memcpy(&image->storage_class,p,sizeof(image->storage_class));
520   p+=sizeof(image->storage_class);
521   (void) memcpy(&image->colorspace,p,sizeof(image->colorspace));
522   p+=sizeof(image->colorspace);
523   (void) memcpy(&image->alpha_trait,p,sizeof(image->alpha_trait));
524   p+=sizeof(image->alpha_trait);
525   (void) memcpy(&image->mask,p,sizeof(image->mask));
526   p+=sizeof(image->mask);
527   (void) memcpy(&image->columns,p,sizeof(image->columns));
528   p+=sizeof(image->columns);
529   (void) memcpy(&image->rows,p,sizeof(image->rows));
530   p+=sizeof(image->rows);
531   (void) memcpy(&image->number_channels,p,sizeof(image->number_channels));
532   p+=sizeof(image->number_channels);
533   (void) memcpy(image->channel_map,p,MaxPixelChannels*
534     sizeof(*image->channel_map));
535   p+=MaxPixelChannels*sizeof(*image->channel_map);
536   (void) memcpy(&image->metacontent_extent,p,sizeof(image->metacontent_extent));
537   p+=sizeof(image->metacontent_extent);
538   if (SyncImagePixelCache(image,exception) == MagickFalse)
539     return(MagickFalse);
540   status=AddValueToSplayTree(registry,(const void *) session_key,image);
541   return(status);
542 }
543
544 static MagickBooleanType ReadDistributeCacheMetacontent(SplayTreeInfo *registry,
545   int file,const size_t session_key,ExceptionInfo *exception)
546 {
547   const unsigned char
548     *metacontent;
549
550   Image
551     *image;
552
553   MagickOffsetType
554     count;
555
556   MagickSizeType
557     length;
558
559   RectangleInfo
560     region;
561
562   register const Quantum
563     *p;
564
565   register unsigned char
566     *q;
567
568   unsigned char
569     message[MaxTextExtent];
570
571   /*
572     Read distributed pixel cache metacontent.
573   */
574   image=(Image *) GetValueFromSplayTree(registry,(const void *) session_key);
575   if (image == (Image *) NULL)
576     return(MagickFalse);
577   length=sizeof(region.width)+sizeof(region.height)+sizeof(region.x)+
578     sizeof(region.y)+sizeof(length);
579   count=dpc_read(file,length,message);
580   if (count != (MagickOffsetType) length)
581     return(MagickFalse);
582   q=message;
583   (void) memcpy(&region.width,q,sizeof(region.width));
584   q+=sizeof(region.width);
585   (void) memcpy(&region.height,q,sizeof(region.height));
586   q+=sizeof(region.height);
587   (void) memcpy(&region.x,q,sizeof(region.x));
588   q+=sizeof(region.x);
589   (void) memcpy(&region.y,q,sizeof(region.y));
590   q+=sizeof(region.y);
591   (void) memcpy(&length,q,sizeof(length));
592   q+=sizeof(length);
593   p=GetVirtualPixels(image,region.x,region.y,region.width,region.height,
594     exception);
595   if (p == (const Quantum *) NULL)
596     return(MagickFalse);
597   metacontent=GetVirtualMetacontent(image);
598   count=dpc_send(file,length,metacontent);
599   if (count != (MagickOffsetType) length)
600     return(MagickFalse);
601   return(MagickTrue);
602 }
603
604 static MagickBooleanType ReadDistributeCachePixels(SplayTreeInfo *registry,
605   int file,const size_t session_key,ExceptionInfo *exception)
606 {
607   Image
608     *image;
609
610   MagickOffsetType
611     count;
612
613   MagickSizeType
614     length;
615
616   RectangleInfo
617     region;
618
619   register const Quantum
620     *p;
621
622   register unsigned char
623     *q;
624
625   unsigned char
626     message[MaxTextExtent];
627
628   /*
629     Read distributed pixel cache pixels.
630   */
631   image=(Image *) GetValueFromSplayTree(registry,(const void *) session_key);
632   if (image == (Image *) NULL)
633     return(MagickFalse);
634   length=sizeof(region.width)+sizeof(region.height)+sizeof(region.x)+
635     sizeof(region.y)+sizeof(length);
636   count=dpc_read(file,length,message);
637   if (count != (MagickOffsetType) length)
638     return(MagickFalse);
639   q=message;
640   (void) memcpy(&region.width,q,sizeof(region.width));
641   q+=sizeof(region.width);
642   (void) memcpy(&region.height,q,sizeof(region.height));
643   q+=sizeof(region.height);
644   (void) memcpy(&region.x,q,sizeof(region.x));
645   q+=sizeof(region.x);
646   (void) memcpy(&region.y,q,sizeof(region.y));
647   q+=sizeof(region.y);
648   (void) memcpy(&length,q,sizeof(length));
649   q+=sizeof(length);
650   p=GetVirtualPixels(image,region.x,region.y,region.width,region.height,
651     exception);
652   if (p == (const Quantum *) NULL)
653     return(MagickFalse);
654   count=dpc_send(file,length,(unsigned char *) p);
655   if (count != (MagickOffsetType) length)
656     return(MagickFalse);
657   return(MagickTrue);
658 }
659
660 static void *RelinquishImageRegistry(void *image)
661 {
662   return((void *) DestroyImageList((Image *) image));
663 }
664
665 static MagickBooleanType WriteDistributeCacheMetacontent(
666   SplayTreeInfo *registry,int file,const size_t session_key,
667   ExceptionInfo *exception)
668 {
669   Image
670     *image;
671
672   MagickOffsetType
673     count;
674
675   MagickSizeType
676     length;
677
678   RectangleInfo
679     region;
680
681   register Quantum
682     *q;
683
684   register unsigned char
685     *p;
686
687   unsigned char
688     message[MaxTextExtent],
689     *metacontent;
690
691   /*
692     Write distributed pixel cache metacontent.
693   */
694   image=(Image *) GetValueFromSplayTree(registry,(const void *) session_key);
695   if (image == (Image *) NULL)
696     return(MagickFalse);
697   length=sizeof(region.width)+sizeof(region.height)+sizeof(region.x)+
698     sizeof(region.y)+sizeof(length);
699   count=dpc_read(file,length,message);
700   if (count != (MagickOffsetType) length)
701     return(MagickFalse);
702   p=message;
703   (void) memcpy(&region.width,p,sizeof(region.width));
704   p+=sizeof(region.width);
705   (void) memcpy(&region.height,p,sizeof(region.height));
706   p+=sizeof(region.height);
707   (void) memcpy(&region.x,p,sizeof(region.x));
708   p+=sizeof(region.x);
709   (void) memcpy(&region.y,p,sizeof(region.y));
710   p+=sizeof(region.y);
711   (void) memcpy(&length,p,sizeof(length));
712   p+=sizeof(length);
713   q=GetAuthenticPixels(image,region.x,region.y,region.width,region.height,
714     exception);
715   if (q == (Quantum *) NULL)
716     return(MagickFalse);
717   metacontent=GetAuthenticMetacontent(image);
718   count=dpc_read(file,length,metacontent);
719   if (count != (MagickOffsetType) length)
720     return(MagickFalse);
721   return(SyncAuthenticPixels(image,exception));
722 }
723
724 static MagickBooleanType WriteDistributeCachePixels(SplayTreeInfo *registry,
725   int file,const size_t session_key,ExceptionInfo *exception)
726 {
727   Image
728     *image;
729
730   MagickOffsetType
731     count;
732
733   MagickSizeType
734     length;
735
736   RectangleInfo
737     region;
738
739   register Quantum
740     *q;
741
742   register unsigned char
743     *p;
744
745   unsigned char
746     message[MaxTextExtent];
747
748   /*
749     Write distributed pixel cache pixels.
750   */
751   image=(Image *) GetValueFromSplayTree(registry,(const void *) session_key);
752   if (image == (Image *) NULL)
753     return(MagickFalse);
754   length=sizeof(region.width)+sizeof(region.height)+sizeof(region.x)+
755     sizeof(region.y)+sizeof(length);
756   count=dpc_read(file,length,message);
757   if (count != (MagickOffsetType) length)
758     return(MagickFalse);
759   p=message;
760   (void) memcpy(&region.width,p,sizeof(region.width));
761   p+=sizeof(region.width);
762   (void) memcpy(&region.height,p,sizeof(region.height));
763   p+=sizeof(region.height);
764   (void) memcpy(&region.x,p,sizeof(region.x));
765   p+=sizeof(region.x);
766   (void) memcpy(&region.y,p,sizeof(region.y));
767   p+=sizeof(region.y);
768   (void) memcpy(&length,p,sizeof(length));
769   p+=sizeof(length);
770   q=GetAuthenticPixels(image,region.x,region.y,region.width,region.height,
771     exception);
772   if (q == (Quantum *) NULL)
773     return(MagickFalse);
774   count=dpc_read(file,length,(unsigned char *) q);
775   if (count != (MagickOffsetType) length)
776     return(MagickFalse);
777   return(SyncAuthenticPixels(image,exception));
778 }
779
780 static void *DistributePixelCacheClient(void *socket)
781 {
782   const char
783     *shared_secret;
784
785   ExceptionInfo
786     *exception;
787
788   int
789     client_socket;
790
791   MagickBooleanType
792     status;
793
794   MagickOffsetType
795     count;
796
797   register unsigned char
798     *p;
799
800   RandomInfo
801     *random_info;
802
803   size_t
804     key,
805     session_key;
806     signature;
807
808   SplayTreeInfo
809     *registry;
810
811   StringInfo
812     *secret;
813
814   unsigned char
815     command,
816     session[2*MaxTextExtent];
817
818   /*
819     Distributed pixel cache client.
820   */
821   shared_secret=GetPolicyValue("shared-secret");
822   if (shared_secret == (const char *) NULL)
823     ThrowFatalException(CacheFatalError,"shared secret expected");
824   p=session;
825   (void) CopyMagickString((char *) p,shared_secret,MaxTextExtent);
826   p+=strlen(shared_secret);
827   random_info=AcquireRandomInfo();
828   secret=GetRandomKey(random_info,DPCSessionKeyLength);
829   (void) memcpy(p,GetStringInfoDatum(secret),DPCSessionKeyLength);
830   p+=DPCSessionKeyLength;
831   signature=MagickLibVersion;
832   (void) memcpy(p,&signature,sizeof(signature));
833   p+=sizeof(signature);
834   signature=MAGICKCORE_QUANTUM_DEPTH;
835   (void) memcpy(p,&signature,sizeof(signature));
836   p+=sizeof(signature);
837   signature=MAGICKCORE_HDRI_ENABLE;
838   (void) memcpy(p,&signature,sizeof(signature));
839   p+=sizeof(signature);
840   session_key=CRC64(session,p-session);
841   random_info=DestroyRandomInfo(random_info);
842   exception=AcquireExceptionInfo();
843   registry=NewSplayTree((int (*)(const void *,const void *)) NULL,
844     (void *(*)(void *)) NULL,RelinquishImageRegistry);
845   client_socket=(*(int *) socket);
846   count=dpc_send(client_socket,DPCSessionKeyLength,GetStringInfoDatum(secret));
847   secret=DestroyStringInfo(secret);
848   for ( ; ; )
849   {
850     count=dpc_read(client_socket,1,(unsigned char *) &command);
851     if (count <= 0)
852       break;
853     count=dpc_read(client_socket,sizeof(key),(unsigned char *) &key);
854     if ((count != (MagickOffsetType) sizeof(key)) || (key != session_key))
855       break;
856     status=MagickFalse;
857     switch (command)
858     {
859       case 'o':
860       {
861         status=OpenDistributeCache(registry,client_socket,session_key,
862           exception);
863         count=dpc_send(client_socket,sizeof(status),(unsigned char *) &status);
864         break;
865       }
866       case 'r':
867       {
868         status=ReadDistributeCachePixels(registry,client_socket,session_key,
869           exception);
870         break;
871       }
872       case 'R':
873       {
874         status=ReadDistributeCacheMetacontent(registry,client_socket,
875           session_key,exception);
876         break;
877       }
878       case 'w':
879       {
880         status=WriteDistributeCachePixels(registry,client_socket,session_key,
881           exception);
882         break;
883       }
884       case 'W':
885       {
886         status=WriteDistributeCacheMetacontent(registry,client_socket,
887           session_key,exception);
888         break;
889       }
890       case 'd':
891       {
892         status=DestroyDistributeCache(registry,client_socket,session_key);
893         break;
894       }
895       default:
896         break;
897     }
898     if (status == MagickFalse)
899       break;
900     if (command == 'd')
901       break;
902   }
903   count=dpc_send(client_socket,sizeof(status),(unsigned char *) &status);
904   (void) close(client_socket);
905   exception=DestroyExceptionInfo(exception);
906   registry=DestroySplayTree(registry);
907   return((void *) NULL);
908 }
909
910 MagickExport void DistributePixelCacheServer(const int port,
911   ExceptionInfo *exception)
912 {
913 #if defined(MAGICKCORE_HAVE_SOCKET) && defined(MAGICKCORE_THREAD_SUPPORT)
914   char
915     service[MaxTextExtent];
916
917   int
918     server_socket,
919     status;
920
921   pthread_attr_t
922     attributes;
923
924   pthread_t
925     threads;
926
927   register struct addrinfo
928     *p;
929
930   struct addrinfo
931     hint,
932     *result;
933
934   struct sockaddr_in
935     address;
936
937   /*
938     Launch distributed pixel cache server.
939   */
940   (void) ResetMagickMemory(&hint,0,sizeof(hint));
941   hint.ai_family=AF_INET;
942   hint.ai_socktype=SOCK_STREAM;
943   hint.ai_flags=AI_PASSIVE;
944   (void) FormatLocaleString(service,MaxTextExtent,"%d",port);
945   status=getaddrinfo((const char *) NULL,service,&hint,&result);
946   if (status != 0)
947     ThrowFatalException(CacheFatalError,"UnableToListen");
948   for (p=result; p != (struct addrinfo *) NULL; p=p->ai_next)
949   {
950     int
951       one;
952
953     server_socket=socket(p->ai_family,p->ai_socktype,p->ai_protocol);
954     if (server_socket == -1)
955       continue;
956     one=1;
957     status=setsockopt(server_socket,SOL_SOCKET,SO_REUSEADDR,&one,(socklen_t)
958       sizeof(one));
959     if (status == -1)
960       continue;
961     status=bind(server_socket,p->ai_addr,p->ai_addrlen);
962     if (status == -1)
963       {
964         (void) close(status);
965         continue;
966       }
967     break;
968   }
969   if (p == (struct addrinfo *) NULL)
970     ThrowFatalException(CacheFatalError,"UnableToBind");
971   freeaddrinfo(result);
972   status=listen(server_socket,DPCPendingConnections);
973   if (status != 0)
974     ThrowFatalException(CacheFatalError,"UnableToListen");
975   pthread_attr_init(&attributes);
976   for ( ; ; )
977   {
978     int
979       client_socket;
980
981     socklen_t
982       length;
983
984     length=(socklen_t) sizeof(address);
985     client_socket=accept(server_socket,(struct sockaddr *) &address,&length);
986     if (client_socket == -1)
987       ThrowFatalException(CacheFatalError,"UnableToEstablishConnection");
988     status=pthread_create(&threads,&attributes,DistributePixelCacheClient,
989       (void *) &client_socket);
990     if (status == -1)
991       ThrowFatalException(CacheFatalError,"UnableToCreateClientThread");
992   }
993   (void) close(server_socket);
994 #else
995   ThrowFatalException(MissingDelegateError,"distributed pixel cache");
996 #endif
997 }
998 \f
999 /*
1000 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1001 %                                                                             %
1002 %                                                                             %
1003 %                                                                             %
1004 +   G e t D i s t r i b u t e C a c h e F i l e                               %
1005 %                                                                             %
1006 %                                                                             %
1007 %                                                                             %
1008 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1009 %
1010 %  GetDistributeCacheFile() returns the file associated with this
1011 %  DistributeCacheInfo structure.
1012 %
1013 %  The format of the GetDistributeCacheFile method is:
1014 %
1015 %      int GetDistributeCacheFile(const DistributeCacheInfo *server_info)
1016 %
1017 %  A description of each parameter follows:
1018 %
1019 %    o server_info: the distributed cache info.
1020 %
1021 */
1022 MagickPrivate int GetDistributeCacheFile(const DistributeCacheInfo *server_info)
1023 {
1024   assert(server_info != (DistributeCacheInfo *) NULL);
1025   assert(server_info->signature == MagickSignature);
1026   return(server_info->file);
1027 }
1028 \f
1029 /*
1030 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1031 %                                                                             %
1032 %                                                                             %
1033 %                                                                             %
1034 +   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                       %
1035 %                                                                             %
1036 %                                                                             %
1037 %                                                                             %
1038 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1039 %
1040 %  GetDistributeCacheHostname() returns the hostname associated with this
1041 %  DistributeCacheInfo structure.
1042 %
1043 %  The format of the GetDistributeCacheHostname method is:
1044 %
1045 %      const char *GetDistributeCacheHostname(
1046 %        const DistributeCacheInfo *server_info)
1047 %
1048 %  A description of each parameter follows:
1049 %
1050 %    o server_info: the distributed cache info.
1051 %
1052 */
1053 MagickPrivate const char *GetDistributeCacheHostname(
1054   const DistributeCacheInfo *server_info)
1055 {
1056   assert(server_info != (DistributeCacheInfo *) NULL);
1057   assert(server_info->signature == MagickSignature);
1058   return(server_info->hostname);
1059 }
1060 \f
1061 /*
1062 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1063 %                                                                             %
1064 %                                                                             %
1065 %                                                                             %
1066 +   G e t D i s t r i b u t e C a c h e P o r t                               %
1067 %                                                                             %
1068 %                                                                             %
1069 %                                                                             %
1070 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1071 %
1072 %  GetDistributeCachePort() returns the port associated with this
1073 %  DistributeCacheInfo structure.
1074 %
1075 %  The format of the GetDistributeCachePort method is:
1076 %
1077 %      int GetDistributeCachePort(const DistributeCacheInfo *server_info)
1078 %
1079 %  A description of each parameter follows:
1080 %
1081 %    o server_info: the distributed cache info.
1082 %
1083 */
1084 MagickPrivate int GetDistributeCachePort(const DistributeCacheInfo *server_info)
1085 {
1086   assert(server_info != (DistributeCacheInfo *) NULL);
1087   assert(server_info->signature == MagickSignature);
1088   return(server_info->port);
1089 }
1090 \f
1091 /*
1092 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1093 %                                                                             %
1094 %                                                                             %
1095 %                                                                             %
1096 +   O p e n D i s t r i b u t e P i x e l C a c h e                           %
1097 %                                                                             %
1098 %                                                                             %
1099 %                                                                             %
1100 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1101 %
1102 %  OpenDistributePixelCache() opens a pixel cache on a remote server.
1103 %
1104 %  The format of the OpenDistributePixelCache method is:
1105 %
1106 %      MagickBooleanType *OpenDistributePixelCache(
1107 %        DistributeCacheInfo *server_info,Image *image)
1108 %
1109 %  A description of each parameter follows:
1110 %
1111 %    o server_info: the distributed cache info.
1112 %
1113 %    o image: the image.
1114 %
1115 */
1116 MagickPrivate MagickBooleanType OpenDistributePixelCache(
1117   DistributeCacheInfo *server_info,Image *image)
1118 {
1119   MagickBooleanType
1120     status;
1121
1122   MagickOffsetType
1123     count;
1124
1125   register unsigned char
1126     *p;
1127
1128   unsigned char
1129     message[MaxTextExtent];
1130
1131   /*
1132     Open distributed pixel cache.
1133   */
1134   assert(server_info != (DistributeCacheInfo *) NULL);
1135   assert(server_info->signature == MagickSignature);
1136   assert(image != (Image *) NULL);
1137   assert(image->signature == MagickSignature);
1138   p=message;
1139   *p++='o';  /* open */
1140   /*
1141     Serialize image attributes (see ValidatePixelCacheMorphology()).
1142   */
1143   (void) memcpy(p,&server_info->session_key,sizeof(server_info->session_key));
1144   p+=sizeof(server_info->session_key);
1145   (void) memcpy(p,&image->storage_class,sizeof(image->storage_class));
1146   p+=sizeof(image->storage_class);
1147   (void) memcpy(p,&image->colorspace,sizeof(image->colorspace));
1148   p+=sizeof(image->colorspace);
1149   (void) memcpy(p,&image->alpha_trait,sizeof(image->alpha_trait));
1150   p+=sizeof(image->alpha_trait);
1151   (void) memcpy(p,&image->mask,sizeof(image->mask));
1152   p+=sizeof(image->mask);
1153   (void) memcpy(p,&image->columns,sizeof(image->columns));
1154   p+=sizeof(image->columns);
1155   (void) memcpy(p,&image->rows,sizeof(image->rows));
1156   p+=sizeof(image->rows);
1157   (void) memcpy(p,&image->number_channels,sizeof(image->number_channels));
1158   p+=sizeof(image->number_channels);
1159   (void) memcpy(p,image->channel_map,MaxPixelChannels*
1160     sizeof(*image->channel_map));
1161   p+=MaxPixelChannels*sizeof(*image->channel_map);
1162   (void) memcpy(p,&image->metacontent_extent,sizeof(image->metacontent_extent));
1163   p+=sizeof(image->metacontent_extent);
1164   count=dpc_send(server_info->file,p-message,message);
1165   if (count != (MagickOffsetType) (p-message))
1166     return(MagickFalse);
1167   status=MagickFalse;
1168   count=dpc_read(server_info->file,sizeof(status),(unsigned char *) &status);
1169   if (count != (MagickOffsetType) sizeof(status))
1170     return(MagickFalse);
1171   return(status);
1172 }
1173 \f
1174 /*
1175 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1176 %                                                                             %
1177 %                                                                             %
1178 %                                                                             %
1179 +   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     %
1180 %                                                                             %
1181 %                                                                             %
1182 %                                                                             %
1183 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1184 %
1185 %  ReadDistributePixelCacheMetacontents() reads metacontent from the specified
1186 %  region of the distributed pixel cache.
1187 %
1188 %  The format of the ReadDistributePixelCacheMetacontents method is:
1189 %
1190 %      MagickOffsetType ReadDistributePixelCacheMetacontents(
1191 %        DistributeCacheInfo *server_info,const RectangleInfo *region,
1192 %        const MagickSizeType length,unsigned char *metacontent)
1193 %
1194 %  A description of each parameter follows:
1195 %
1196 %    o server_info: the distributed cache info.
1197 %
1198 %    o image: the image.
1199 %
1200 %    o region: read the metacontent from this region of the image.
1201 %
1202 %    o length: the length in bytes of the metacontent.
1203 %
1204 %    o metacontent: read these metacontent from the pixel cache.
1205 %
1206 */
1207 MagickPrivate MagickOffsetType ReadDistributePixelCacheMetacontent(
1208   DistributeCacheInfo *server_info,const RectangleInfo *region,
1209   const MagickSizeType length,unsigned char *metacontent)
1210 {
1211   MagickOffsetType
1212     count;
1213
1214   register unsigned char
1215     *p;
1216
1217   unsigned char
1218     message[MaxTextExtent];
1219
1220   /*
1221     Read distributed pixel cache metacontent.
1222   */
1223   assert(server_info != (DistributeCacheInfo *) NULL);
1224   assert(server_info->signature == MagickSignature);
1225   assert(region != (RectangleInfo *) NULL);
1226   assert(metacontent != (unsigned char *) NULL);
1227   if (length > (MagickSizeType) SSIZE_MAX)
1228     return(-1);
1229   p=message;
1230   *p++='R';
1231   (void) memcpy(p,&server_info->session_key,sizeof(server_info->session_key));
1232   p+=sizeof(server_info->session_key);
1233   (void) memcpy(p,&region->width,sizeof(region->width));
1234   p+=sizeof(region->width);
1235   (void) memcpy(p,&region->height,sizeof(region->height));
1236   p+=sizeof(region->height);
1237   (void) memcpy(p,&region->x,sizeof(region->x));
1238   p+=sizeof(region->x);
1239   (void) memcpy(p,&region->y,sizeof(region->y));
1240   p+=sizeof(region->y);
1241   (void) memcpy(p,&length,sizeof(length));
1242   p+=sizeof(length);
1243   count=dpc_send(server_info->file,p-message,message);
1244   if (count != (MagickOffsetType) (p-message))
1245     return(-1);
1246   return(dpc_read(server_info->file,length,metacontent));
1247 }
1248 \f
1249 /*
1250 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1251 %                                                                             %
1252 %                                                                             %
1253 %                                                                             %
1254 +   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               %
1255 %                                                                             %
1256 %                                                                             %
1257 %                                                                             %
1258 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1259 %
1260 %  ReadDistributePixelCachePixels() reads pixels from the specified region of
1261 %  the distributed pixel cache.
1262 %
1263 %  The format of the ReadDistributePixelCachePixels method is:
1264 %
1265 %      MagickOffsetType ReadDistributePixelCachePixels(
1266 %        DistributeCacheInfo *server_info,const RectangleInfo *region,
1267 %        const MagickSizeType length,unsigned char *pixels)
1268 %
1269 %  A description of each parameter follows:
1270 %
1271 %    o server_info: the distributed cache info.
1272 %
1273 %    o image: the image.
1274 %
1275 %    o region: read the pixels from this region of the image.
1276 %
1277 %    o length: the length in bytes of the pixels.
1278 %
1279 %    o pixels: read these pixels from the pixel cache.
1280 %
1281 */
1282 MagickPrivate MagickOffsetType ReadDistributePixelCachePixels(
1283   DistributeCacheInfo *server_info,const RectangleInfo *region,
1284   const MagickSizeType length,unsigned char *pixels)
1285 {
1286   MagickOffsetType
1287     count;
1288
1289   register unsigned char
1290     *p;
1291
1292   unsigned char
1293     message[MaxTextExtent];
1294
1295   /*
1296     Read distributed pixel cache pixels.
1297   */
1298   assert(server_info != (DistributeCacheInfo *) NULL);
1299   assert(server_info->signature == MagickSignature);
1300   assert(region != (RectangleInfo *) NULL);
1301   assert(pixels != (unsigned char *) NULL);
1302   if (length > (MagickSizeType) SSIZE_MAX)
1303     return(-1);
1304   p=message;
1305   *p++='r';
1306   (void) memcpy(p,&server_info->session_key,sizeof(server_info->session_key));
1307   p+=sizeof(server_info->session_key);
1308   (void) memcpy(p,&region->width,sizeof(region->width));
1309   p+=sizeof(region->width);
1310   (void) memcpy(p,&region->height,sizeof(region->height));
1311   p+=sizeof(region->height);
1312   (void) memcpy(p,&region->x,sizeof(region->x));
1313   p+=sizeof(region->x);
1314   (void) memcpy(p,&region->y,sizeof(region->y));
1315   p+=sizeof(region->y);
1316   (void) memcpy(p,&length,sizeof(length));
1317   p+=sizeof(length);
1318   count=dpc_send(server_info->file,p-message,message);
1319   if (count != (MagickOffsetType) (p-message))
1320     return(-1);
1321   return(dpc_read(server_info->file,length,pixels));
1322 }
1323 \f
1324 /*
1325 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1326 %                                                                             %
1327 %                                                                             %
1328 %                                                                             %
1329 +   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               %
1330 %                                                                             %
1331 %                                                                             %
1332 %                                                                             %
1333 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1334 %
1335 %  RelinquishDistributePixelCache() frees resources acquired with
1336 %  OpenDistributePixelCache().
1337 %
1338 %  The format of the RelinquishDistributePixelCache method is:
1339 %
1340 %      MagickBooleanType RelinquishDistributePixelCache(
1341 %        DistributeCacheInfo *server_info)
1342 %
1343 %  A description of each parameter follows:
1344 %
1345 %    o server_info: the distributed cache info.
1346 %
1347 */
1348 MagickPrivate MagickBooleanType RelinquishDistributePixelCache(
1349   DistributeCacheInfo *server_info)
1350 {
1351   MagickBooleanType
1352     status;
1353
1354   MagickOffsetType
1355     count;
1356
1357   register unsigned char
1358     *p;
1359
1360   unsigned char
1361     message[MaxTextExtent];
1362
1363   /*
1364     Delete distributed pixel cache.
1365   */
1366   assert(server_info != (DistributeCacheInfo *) NULL);
1367   assert(server_info->signature == MagickSignature);
1368   p=message;
1369   *p++='d';
1370   (void) memcpy(p,&server_info->session_key,sizeof(server_info->session_key));
1371   p+=sizeof(server_info->session_key);
1372   count=dpc_send(server_info->file,p-message,message);
1373   if (count != (MagickOffsetType) (p-message))
1374     return(MagickFalse);
1375   count=dpc_read(server_info->file,sizeof(status),(unsigned char *) &status);
1376   if (count != (MagickOffsetType) sizeof(status))
1377     return(MagickFalse);
1378   return(status);
1379 }
1380 \f
1381 /*
1382 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1383 %                                                                             %
1384 %                                                                             %
1385 %                                                                             %
1386 +   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   %
1387 %                                                                             %
1388 %                                                                             %
1389 %                                                                             %
1390 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1391 %
1392 %  WriteDistributePixelCacheMetacontents() writes image metacontent to the
1393 %  specified region of the distributed pixel cache.
1394 %
1395 %  The format of the WriteDistributePixelCacheMetacontents method is:
1396 %
1397 %      MagickOffsetType WriteDistributePixelCacheMetacontents(
1398 %        DistributeCacheInfo *server_info,const RectangleInfo *region,
1399 %        const MagickSizeType length,const unsigned char *metacontent)
1400 %
1401 %  A description of each parameter follows:
1402 %
1403 %    o server_info: the distributed cache info.
1404 %
1405 %    o image: the image.
1406 %
1407 %    o region: write the metacontent to this region of the image.
1408 %
1409 %    o length: the length in bytes of the metacontent.
1410 %
1411 %    o metacontent: write these metacontent to the pixel cache.
1412 %
1413 */
1414 MagickPrivate MagickOffsetType WriteDistributePixelCacheMetacontent(
1415   DistributeCacheInfo *server_info,const RectangleInfo *region,
1416   const MagickSizeType length,const unsigned char *metacontent)
1417 {
1418   MagickOffsetType
1419     count;
1420
1421   register unsigned char
1422     *p;
1423
1424   unsigned char
1425     message[MaxTextExtent];
1426
1427   /*
1428     Write distributed pixel cache metacontent.
1429   */
1430   assert(server_info != (DistributeCacheInfo *) NULL);
1431   assert(server_info->signature == MagickSignature);
1432   assert(region != (RectangleInfo *) NULL);
1433   assert(metacontent != (unsigned char *) NULL);
1434   if (length > (MagickSizeType) SSIZE_MAX)
1435     return(-1);
1436   p=message;
1437   *p++='W';
1438   (void) memcpy(p,&server_info->session_key,sizeof(server_info->session_key));
1439   p+=sizeof(server_info->session_key);
1440   (void) memcpy(p,&region->width,sizeof(region->width));
1441   p+=sizeof(region->width);
1442   (void) memcpy(p,&region->height,sizeof(region->height));
1443   p+=sizeof(region->height);
1444   (void) memcpy(p,&region->x,sizeof(region->x));
1445   p+=sizeof(region->x);
1446   (void) memcpy(p,&region->y,sizeof(region->y));
1447   p+=sizeof(region->y);
1448   (void) memcpy(p,&length,sizeof(length));
1449   p+=sizeof(length);
1450   count=dpc_send(server_info->file,p-message,message);
1451   if (count != (MagickOffsetType) (p-message))
1452     return(-1);
1453   return(dpc_send(server_info->file,length,metacontent));
1454 }
1455 \f
1456 /*
1457 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1458 %                                                                             %
1459 %                                                                             %
1460 %                                                                             %
1461 +   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             %
1462 %                                                                             %
1463 %                                                                             %
1464 %                                                                             %
1465 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1466 %
1467 %  WriteDistributePixelCachePixels() writes image pixels to the specified
1468 %  region of the distributed pixel cache.
1469 %
1470 %  The format of the WriteDistributePixelCachePixels method is:
1471 %
1472 %      MagickBooleanType WriteDistributePixelCachePixels(
1473 %        DistributeCacheInfo *server_info,const RectangleInfo *region,
1474 %        const MagickSizeType length,const unsigned char *pixels)
1475 %
1476 %  A description of each parameter follows:
1477 %
1478 %    o server_info: the distributed cache info.
1479 %
1480 %    o image: the image.
1481 %
1482 %    o region: write the pixels to this region of the image.
1483 %
1484 %    o length: the length in bytes of the pixels.
1485 %
1486 %    o pixels: write these pixels to the pixel cache.
1487 %
1488 */
1489 MagickPrivate MagickOffsetType WriteDistributePixelCachePixels(
1490   DistributeCacheInfo *server_info,const RectangleInfo *region,
1491   const MagickSizeType length,const unsigned char *pixels)
1492 {
1493   MagickOffsetType
1494     count;
1495
1496   register unsigned char
1497     *p;
1498
1499   unsigned char
1500     message[MaxTextExtent];
1501
1502   /*
1503     Write distributed pixel cache pixels.
1504   */
1505   assert(server_info != (DistributeCacheInfo *) NULL);
1506   assert(server_info->signature == MagickSignature);
1507   assert(region != (RectangleInfo *) NULL);
1508   assert(pixels != (const unsigned char *) NULL);
1509   if (length > (MagickSizeType) SSIZE_MAX)
1510     return(-1);
1511   p=message;
1512   *p++='w';
1513   (void) memcpy(p,&server_info->session_key,sizeof(server_info->session_key));
1514   p+=sizeof(server_info->session_key);
1515   (void) memcpy(p,&region->width,sizeof(region->width));
1516   p+=sizeof(region->width);
1517   (void) memcpy(p,&region->height,sizeof(region->height));
1518   p+=sizeof(region->height);
1519   (void) memcpy(p,&region->x,sizeof(region->x));
1520   p+=sizeof(region->x);
1521   (void) memcpy(p,&region->y,sizeof(region->y));
1522   p+=sizeof(region->y);
1523   (void) memcpy(p,&length,sizeof(length));
1524   p+=sizeof(length);
1525   count=dpc_send(server_info->file,p-message,message);
1526   if (count != (MagickOffsetType) (p-message))
1527     return(-1);
1528   return(dpc_send(server_info->file,length,pixels));
1529 }