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