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