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