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