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