distribute-cache.c revision ba3f4649e495bdba70995832cf747e026e86024b
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
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
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
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
122static inline MagickSizeType MagickMin(const MagickSizeType x,
123  const MagickSizeType y)
124{
125  if (x < y)
126    return(x);
127  return(y);
128}
129
130static 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
159static 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
245static 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
300MagickPrivate 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
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*/
358MagickPrivate 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
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
396static 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
406static 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
438static 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
504static 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
564static 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
620static void *RelinquishImageRegistry(void *image)
621{
622  return((void *) DestroyImageList((Image *) image));
623}
624
625static 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
684static 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
740static 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
859MagickExport 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
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*/
974MagickPrivate 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
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*/
1005MagickPrivate 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
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*/
1036MagickPrivate 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
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*/
1068MagickPrivate 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
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*/
1161MagickPrivate 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
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*/
1236MagickPrivate 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
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*/
1302MagickPrivate 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
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*/
1368MagickPrivate 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
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*/
1443MagickPrivate 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}
1484