distribute-cache.c revision e27c19696517e8ec47c9927d86a83b8483de1313
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 DestroyDistributeCache(SplayTreeInfo *image_registry,
383  int file,const MagickSizeType session_key)
384{
385  return(DeleteNodeFromSplayTree(image_registry,(const void *) session_key));
386}
387
388static MagickBooleanType OpenDistributeCache(SplayTreeInfo *image_registry,
389  int file,const MagickSizeType session_key)
390{
391  ExceptionInfo
392    *exception;
393
394  Image
395    *image;
396
397  MagickBooleanType
398    status;
399
400  register unsigned char
401    *p;
402
403  size_t
404    length;
405
406  ssize_t
407    count;
408
409  unsigned char
410    buffer[MaxTextExtent];
411
412  exception=AcquireExceptionInfo();
413  image=AcquireImage((ImageInfo *) NULL,exception);
414  exception=DestroyExceptionInfo(exception);
415  if (image == (Image *) NULL)
416    return(MagickFalse);
417  length=sizeof(image->columns)+sizeof(image->rows)+
418    sizeof(image->number_channels);
419  count=read(file,buffer,length);
420  if (count != (ssize_t) length)
421    return(MagickFalse);
422  p=buffer;
423  (void) memcpy(&image->columns,p,sizeof(image->columns));
424  p+=sizeof(image->columns);
425  (void) memcpy(&image->rows,p,sizeof(image->rows));
426  p+=sizeof(image->rows);
427  (void) memcpy(&image->number_channels,p,sizeof(image->number_channels));
428  p+=sizeof(image->number_channels);
429  status=AddValueToSplayTree(image_registry,(const void *) session_key,image);
430  return(status);
431}
432
433static MagickBooleanType ReadDistributeCacheMetacontent(
434  SplayTreeInfo *image_registry,int file,const MagickSizeType session_key)
435{
436  const unsigned char
437    *metacontent;
438
439  ExceptionInfo
440    *exception;
441
442  Image
443    *image;
444
445  RectangleInfo
446    region;
447
448  register const Quantum
449    *p;
450
451  register unsigned char
452    *q;
453
454  size_t
455    length;
456
457  ssize_t
458    count;
459
460  unsigned char
461    buffer[MaxTextExtent];
462
463  image=(Image *) GetValueFromSplayTree(image_registry,(const void *)
464    session_key);
465  if (image == (Image *) NULL)
466    return(MagickFalse);
467  length=sizeof(region.width)+sizeof(region.height)+sizeof(region.x)+
468    sizeof(region.y)+sizeof(length);
469  count=read(file,buffer,length);
470  if (count != (ssize_t) length)
471    return(MagickFalse);
472  q=buffer;
473  (void) memcpy(&region.width,q,sizeof(region.width));
474  q+=sizeof(region.width);
475  (void) memcpy(&region.height,q,sizeof(region.height));
476  q+=sizeof(region.width);
477  (void) memcpy(&region.x,q,sizeof(region.x));
478  q+=sizeof(region.width);
479  (void) memcpy(&region.y,q,sizeof(region.y));
480  q+=sizeof(region.width);
481  (void) memcpy(&length,q,sizeof(length));
482  q+=sizeof(length);
483  exception=AcquireExceptionInfo();
484  p=GetVirtualPixels(image,region.x,region.y,region.width,region.height,
485    exception);
486  exception=DestroyExceptionInfo(exception);
487  if (p == (const Quantum *) NULL)
488    return(MagickFalse);
489  metacontent=GetVirtualMetacontent(image);
490  count=write(file,metacontent,length);
491  if (count != (ssize_t) length)
492    return(MagickFalse);
493  return(MagickTrue);
494}
495
496static MagickBooleanType ReadDistributeCachePixels(
497  SplayTreeInfo *image_registry,int file,const MagickSizeType session_key)
498{
499  ExceptionInfo
500    *exception;
501
502  Image
503    *image;
504
505  RectangleInfo
506    region;
507
508  register const Quantum
509    *p;
510
511  register unsigned char
512    *q;
513
514  size_t
515    length;
516
517  ssize_t
518    count;
519
520  unsigned char
521    buffer[MaxTextExtent];
522
523  image=(Image *) GetValueFromSplayTree(image_registry,(const void *)
524    session_key);
525  if (image == (Image *) NULL)
526    return(MagickFalse);
527  length=sizeof(region.width)+sizeof(region.height)+sizeof(region.x)+
528    sizeof(region.y)+sizeof(length);
529  count=read(file,buffer,length);
530  if (count != (ssize_t) length)
531    return(MagickFalse);
532  q=buffer;
533  (void) memcpy(&region.width,q,sizeof(region.width));
534  q+=sizeof(region.width);
535  (void) memcpy(&region.height,q,sizeof(region.height));
536  q+=sizeof(region.width);
537  (void) memcpy(&region.x,q,sizeof(region.x));
538  q+=sizeof(region.width);
539  (void) memcpy(&region.y,q,sizeof(region.y));
540  q+=sizeof(region.width);
541  (void) memcpy(&length,q,sizeof(length));
542  q+=sizeof(length);
543  exception=AcquireExceptionInfo();
544  p=GetVirtualPixels(image,region.x,region.y,region.width,region.height,
545    exception);
546  exception=DestroyExceptionInfo(exception);
547  if (p == (const Quantum *) NULL)
548    return(MagickFalse);
549  count=write(file,p,length);
550  if (count != (ssize_t) length)
551    return(MagickFalse);
552  return(MagickTrue);
553}
554
555static MagickBooleanType WriteDistributeCacheMetacontent(
556  SplayTreeInfo *image_registry,int file,const MagickSizeType session_key)
557{
558  ExceptionInfo
559    *exception;
560
561  Image
562    *image;
563
564  MagickBooleanType
565    status;
566
567  RectangleInfo
568    region;
569
570  register Quantum
571    *q;
572
573  register unsigned char
574    *p;
575
576  size_t
577    length;
578
579  ssize_t
580    count;
581
582  unsigned char
583    buffer[MaxTextExtent],
584    *metacontent;
585
586  image=(Image *) GetValueFromSplayTree(image_registry,(const void *)
587    session_key);
588  if (image == (Image *) NULL)
589    return(MagickFalse);
590  length=sizeof(region.width)+sizeof(region.height)+sizeof(region.x)+
591    sizeof(region.y)+sizeof(length);
592  count=read(file,buffer,length);
593  if (count != (ssize_t) length)
594    return(MagickFalse);
595  p=buffer;
596  (void) memcpy(&region.width,p,sizeof(region.width));
597  p+=sizeof(region.width);
598  (void) memcpy(&region.height,p,sizeof(region.height));
599  p+=sizeof(region.width);
600  (void) memcpy(&region.x,p,sizeof(region.x));
601  p+=sizeof(region.width);
602  (void) memcpy(&region.y,p,sizeof(region.y));
603  p+=sizeof(region.width);
604  (void) memcpy(&length,p,sizeof(length));
605  p+=sizeof(length);
606  exception=AcquireExceptionInfo();
607  q=GetAuthenticPixels(image,region.x,region.y,region.width,region.height,
608    exception);
609  exception=DestroyExceptionInfo(exception);
610  if (q == (Quantum *) NULL)
611    return(MagickFalse);
612  metacontent=GetAuthenticMetacontent(image);
613  count=read(file,metacontent,length);
614  if (count != (ssize_t) length)
615    return(MagickFalse);
616  status=SyncAuthenticPixels(image,exception);
617  return(status);
618}
619
620static MagickBooleanType WriteDistributeCachePixels(
621  SplayTreeInfo *image_registry,int file,const MagickSizeType session_key)
622{
623  ExceptionInfo
624    *exception;
625
626  Image
627    *image;
628
629  MagickBooleanType
630    status;
631
632  RectangleInfo
633    region;
634
635  register Quantum
636    *q;
637
638  register unsigned char
639    *p;
640
641  size_t
642    length;
643
644  ssize_t
645    count;
646
647  unsigned char
648    buffer[MaxTextExtent];
649
650  image=(Image *) GetValueFromSplayTree(image_registry,(const void *)
651    session_key);
652  if (image == (Image *) NULL)
653    return(MagickFalse);
654  length=sizeof(region.width)+sizeof(region.height)+sizeof(region.x)+
655    sizeof(region.y)+sizeof(length);
656  count=read(file,buffer,length);
657  if (count != (ssize_t) length)
658    return(MagickFalse);
659  p=buffer;
660  (void) memcpy(&region.width,p,sizeof(region.width));
661  p+=sizeof(region.width);
662  (void) memcpy(&region.height,p,sizeof(region.height));
663  p+=sizeof(region.width);
664  (void) memcpy(&region.x,p,sizeof(region.x));
665  p+=sizeof(region.width);
666  (void) memcpy(&region.y,p,sizeof(region.y));
667  p+=sizeof(region.width);
668  (void) memcpy(&length,p,sizeof(length));
669  p+=sizeof(length);
670  exception=AcquireExceptionInfo();
671  q=GetAuthenticPixels(image,region.x,region.y,region.width,region.height,
672    exception);
673  exception=DestroyExceptionInfo(exception);
674  if (q == (Quantum *) NULL)
675    return(MagickFalse);
676  count=read(file,q,length);
677  if (count != (ssize_t) length)
678    return(MagickFalse);
679  status=SyncAuthenticPixels(image,exception);
680  return(status);
681}
682
683static void *DistributePixelCacheClient(void *socket)
684{
685  const char
686    *shared_secret;
687
688  int
689    client_socket;
690
691  MagickBooleanType
692    status;
693
694  MagickSizeType
695    key,
696    session_key;
697
698  RandomInfo
699    *random_info;
700
701  SplayTreeInfo
702    *image_registry;
703
704  ssize_t
705    count;
706
707  StringInfo
708    *secret;
709
710  unsigned char
711    command,
712    session[MaxTextExtent];
713
714  /*
715    Generate session key.
716  */
717  shared_secret=GetPolicyValue("shared-secret");
718  if (shared_secret == (const char *) NULL)
719    ThrowFatalException(CacheFatalError,"shared secret expected");
720  (void) CopyMagickString((char *) session,shared_secret,MaxTextExtent-
721    DPCSessionKeyLength);
722  random_info=AcquireRandomInfo();
723  secret=GetRandomKey(random_info,DPCSessionKeyLength);
724  (void) memcpy(session+strlen(shared_secret),GetStringInfoDatum(secret),
725    DPCSessionKeyLength);
726  session_key=CRC64(session,strlen(shared_secret)+DPCSessionKeyLength);
727  random_info=DestroyRandomInfo(random_info);
728  image_registry=NewSplayTree((int (*)(const void *,const void *)) NULL,
729    (void *(*)(void *)) NULL,(void *(*)(void *)) NULL);
730  client_socket=(*(int *) socket);
731  count=write(client_socket,GetStringInfoDatum(secret),DPCSessionKeyLength);
732  secret=DestroyStringInfo(secret);
733  for ( ; ; )
734  {
735    count=read(client_socket,&command,1);
736    if (count <= 0)
737      break;
738    count=read(client_socket,&key,sizeof(key));
739    if ((count != (ssize_t) sizeof(key)) && (key != session_key))
740      break;
741    status=MagickFalse;
742    switch (command)
743    {
744      case 'o':
745      {
746        status=OpenDistributeCache(image_registry,client_socket,session_key);
747        break;
748      }
749      case 'r':
750      {
751        status=ReadDistributeCachePixels(image_registry,client_socket,
752          session_key);
753        break;
754      }
755      case 'R':
756      {
757        status=ReadDistributeCacheMetacontent(image_registry,client_socket,
758          session_key);
759        break;
760      }
761      case 'w':
762      {
763        status=WriteDistributeCachePixels(image_registry,client_socket,
764          session_key);
765        break;
766      }
767      case 'W':
768      {
769        status=WriteDistributeCacheMetacontent(image_registry,client_socket,
770          session_key);
771        break;
772      }
773      case 'd':
774      {
775        status=DestroyDistributeCache(image_registry,client_socket,session_key);
776        break;
777      }
778      default:
779        break;
780    }
781    count=write(client_socket,&status,sizeof(status));
782    if (count != (ssize_t) sizeof(status))
783      break;
784  }
785  (void) close(client_socket);
786  image_registry=DestroySplayTree(image_registry);
787  return((void *) NULL);
788}
789
790MagickExport void DistributePixelCacheServer(const size_t port,
791  ExceptionInfo *exception)
792{
793#if defined(MAGICKCORE_HAVE_SOCKET) && defined(MAGICKCORE_THREAD_SUPPORT)
794  int
795    server_socket,
796    status;
797
798  pthread_attr_t
799    attributes;
800
801  pthread_t
802    threads;
803
804  struct sockaddr_in
805    address;
806
807  /*
808    Launch distributed pixel cache server.
809  */
810  server_socket=socket(AF_INET,SOCK_STREAM,0);
811  address.sin_family=AF_INET;
812  address.sin_port=htons(port);
813  address.sin_addr.s_addr=htonl(INADDR_ANY);
814  status=bind(server_socket,(struct sockaddr *) &address,(socklen_t)
815    sizeof(address));
816  if (status != 0)
817    ThrowFatalException(CacheFatalError,"UnableToBind");
818  status=listen(server_socket,1024);
819  if (status != 0)
820    ThrowFatalException(CacheFatalError,"UnableToListen");
821  pthread_attr_init(&attributes);
822  for ( ; ; )
823  {
824    int
825      client_socket;
826
827    socklen_t
828      length;
829
830    length=(socklen_t) sizeof(address);
831    client_socket=accept(server_socket,(struct sockaddr *) &address,&length);
832    if (client_socket == -1)
833      ThrowFatalException(CacheFatalError,"UnableToEstablishConnection");
834    status=pthread_create(&threads,&attributes,DistributePixelCacheClient,
835      (void *) &client_socket);
836    if (status == -1)
837      ThrowFatalException(CacheFatalError,"UnableToCreateClientThread");
838  }
839  (void) close(server_socket);
840#else
841  ThrowFatalException(MissingDelegateError,"distributed pixel cache");
842#endif
843}
844
845/*
846%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
847%                                                                             %
848%                                                                             %
849%                                                                             %
850+   G e t D i s t r i b u t e C a c h e F i l e                               %
851%                                                                             %
852%                                                                             %
853%                                                                             %
854%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
855%
856%  GetDistributeCacheFile() returns the file associated with this
857%  DistributeCacheInfo structure.
858%
859%  The format of the GetDistributeCacheFile method is:
860%
861%      int GetDistributeCacheFile(
862%        const DistributeCacheInfo *distribute_cache_info)
863%
864%  A description of each parameter follows:
865%
866%    o distribute_cache_info: the distributed cache info.
867%
868*/
869MagickPrivate int GetDistributeCacheFile(
870  const DistributeCacheInfo *distribute_cache_info)
871{
872  assert(distribute_cache_info != (DistributeCacheInfo *) NULL);
873  assert(distribute_cache_info->signature == MagickSignature);
874  return(distribute_cache_info->file);
875}
876
877/*
878%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
879%                                                                             %
880%                                                                             %
881%                                                                             %
882+   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                       %
883%                                                                             %
884%                                                                             %
885%                                                                             %
886%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
887%
888%  GetDistributeCacheHostname() returns the hostname associated with this
889%  DistributeCacheInfo structure.
890%
891%  The format of the GetDistributeCacheHostname method is:
892%
893%      const char *GetDistributeCacheHostname(
894%        const DistributeCacheInfo *distribute_cache_info)
895%
896%  A description of each parameter follows:
897%
898%    o distribute_cache_info: the distributed cache info.
899%
900*/
901MagickPrivate const char *GetDistributeCacheHostname(
902  const DistributeCacheInfo *distribute_cache_info)
903{
904  assert(distribute_cache_info != (DistributeCacheInfo *) NULL);
905  assert(distribute_cache_info->signature == MagickSignature);
906  return(distribute_cache_info->hostname);
907}
908
909/*
910%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
911%                                                                             %
912%                                                                             %
913%                                                                             %
914+   G e t D i s t r i b u t e C a c h e P o r t                               %
915%                                                                             %
916%                                                                             %
917%                                                                             %
918%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
919%
920%  GetDistributeCachePort() returns the port associated with this
921%  DistributeCacheInfo structure.
922%
923%  The format of the GetDistributeCachePort method is:
924%
925%      int GetDistributeCachePort(
926%        const DistributeCacheInfo *distribute_cache_info)
927%
928%  A description of each parameter follows:
929%
930%    o distribute_cache_info: the distributed cache info.
931%
932*/
933MagickPrivate int GetDistributeCachePort(
934  const DistributeCacheInfo *distribute_cache_info)
935{
936  assert(distribute_cache_info != (DistributeCacheInfo *) NULL);
937  assert(distribute_cache_info->signature == MagickSignature);
938  return(distribute_cache_info->port);
939}
940
941/*
942%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
943%                                                                             %
944%                                                                             %
945%                                                                             %
946+   O p e n D i s t r i b u t e P i x e l C a c h e                           %
947%                                                                             %
948%                                                                             %
949%                                                                             %
950%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
951%
952%  OpenDistributePixelCache() opens a pixel cache on a remote server.
953%
954%  The format of the OpenDistributePixelCache method is:
955%
956%      MagickBooleanType *OpenDistributePixelCache(
957%        DistributeCacheInfo *distribute_cache_info,Image *image)
958%
959%  A description of each parameter follows:
960%
961%    o distribute_cache_info: the distributed cache info.
962%
963%    o image: the image.
964%
965*/
966MagickPrivate MagickBooleanType OpenDistributePixelCache(
967  DistributeCacheInfo *distribute_cache_info,Image *image)
968{
969  MagickBooleanType
970    status;
971
972  register unsigned char
973    *p;
974
975  ssize_t
976    count;
977
978  unsigned char
979    buffer[MaxTextExtent];
980
981  assert(distribute_cache_info != (DistributeCacheInfo *) NULL);
982  assert(distribute_cache_info->signature == MagickSignature);
983  assert(image != (Image *) NULL);
984  assert(image->signature == MagickSignature);
985  p=buffer;
986  *p++='o';  /* open */
987  (void) memcpy(p,&distribute_cache_info->session_key,
988    sizeof(distribute_cache_info->session_key));
989  p+=sizeof(distribute_cache_info->session_key);
990  (void) memcpy(p,&image->columns,sizeof(image->columns));
991  p+=sizeof(image->columns);
992  (void) memcpy(p,&image->rows,sizeof(image->rows));
993  p+=sizeof(image->rows);
994  (void) memcpy(p,&image->number_channels,sizeof(image->number_channels));
995  p+=sizeof(image->number_channels);
996  count=write(distribute_cache_info->file,buffer,p-buffer);
997  if (count != (ssize_t) (p-buffer))
998    return(MagickFalse);
999  count=read(distribute_cache_info->file,&status,sizeof(status));
1000  if (count != (ssize_t) sizeof(status))
1001    return(MagickFalse);
1002  return(MagickTrue);
1003}
1004
1005/*
1006%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1007%                                                                             %
1008%                                                                             %
1009%                                                                             %
1010+   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     %
1011%                                                                             %
1012%                                                                             %
1013%                                                                             %
1014%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1015%
1016%  ReadDistributePixelCacheMetacontents() reads metacontent from the specified
1017%  region of the distributed pixel cache.
1018%
1019%  The format of the ReadDistributePixelCacheMetacontents method is:
1020%
1021%      MagickBooleanType *ReadDistributePixelCacheMetacontents(
1022%        DistributeCacheInfo *distribute_cache_info,const RectangleInfo *region,
1023%        const MagickSizeType length,unsigned char *metacontent)
1024%
1025%  A description of each parameter follows:
1026%
1027%    o distribute_cache_info: the distributed cache info.
1028%
1029%    o image: the image.
1030%
1031%    o region: read the metacontent from this region of the image.
1032%
1033%    o length: the length in bytes of the metacontent.
1034%
1035%    o metacontent: read these metacontent from the pixel cache.
1036%
1037*/
1038MagickPrivate MagickBooleanType ReadDistributePixelCacheMetacontent(
1039  DistributeCacheInfo *distribute_cache_info,const RectangleInfo *region,
1040  const MagickSizeType length,unsigned char *metacontent)
1041{
1042  MagickBooleanType
1043    status;
1044
1045  register unsigned char
1046    *p;
1047
1048  ssize_t
1049    count;
1050
1051  unsigned char
1052    buffer[MaxTextExtent];
1053
1054  assert(distribute_cache_info != (DistributeCacheInfo *) NULL);
1055  assert(distribute_cache_info->signature == MagickSignature);
1056  assert(region != (RectangleInfo *) NULL);
1057  assert(metacontent != (unsigned char *) NULL);
1058  assert(length == ((size_t) length));
1059  p=buffer;
1060  *p++='R';  /* read */
1061  (void) memcpy(p,&distribute_cache_info->session_key,
1062    sizeof(distribute_cache_info->session_key));
1063  p+=sizeof(distribute_cache_info->session_key);
1064  (void) memcpy(p,&region->width,sizeof(region->width));
1065  p+=sizeof(region->width);
1066  (void) memcpy(p,&region->height,sizeof(region->height));
1067  p+=sizeof(region->height);
1068  (void) memcpy(p,&region->x,sizeof(region->x));
1069  p+=sizeof(region->x);
1070  (void) memcpy(p,&region->y,sizeof(region->y));
1071  p+=sizeof(region->y);
1072  (void) memcpy(p,&length,sizeof(length));
1073  p+=sizeof(length);
1074  count=write(distribute_cache_info->file,buffer,p-buffer);
1075  if (count != (ssize_t) (p-buffer))
1076    return(MagickFalse);
1077  count=read(distribute_cache_info->file,(unsigned char *) metacontent,(size_t)
1078    length);
1079  if (count != (ssize_t) length)
1080    return(MagickFalse);
1081  count=read(distribute_cache_info->file,&status,sizeof(status));
1082  if (count != (ssize_t) sizeof(status))
1083    return(MagickFalse);
1084  return(status != 0 ? MagickTrue : MagickFalse);
1085}
1086
1087/*
1088%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1089%                                                                             %
1090%                                                                             %
1091%                                                                             %
1092+   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               %
1093%                                                                             %
1094%                                                                             %
1095%                                                                             %
1096%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1097%
1098%  ReadDistributePixelCachePixels() reads pixels from the specified region of
1099%  the distributed pixel cache.
1100%
1101%  The format of the ReadDistributePixelCachePixels method is:
1102%
1103%      MagickBooleanType *ReadDistributePixelCachePixels(
1104%        DistributeCacheInfo *distribute_cache_info,const RectangleInfo *region,
1105%        const MagickSizeType length,unsigned char *pixels)
1106%
1107%  A description of each parameter follows:
1108%
1109%    o distribute_cache_info: the distributed cache info.
1110%
1111%    o image: the image.
1112%
1113%    o region: read the pixels from this region of the image.
1114%
1115%    o length: the length in bytes of the pixels.
1116%
1117%    o pixels: read these pixels from the pixel cache.
1118%
1119*/
1120MagickPrivate MagickBooleanType ReadDistributePixelCachePixels(
1121  DistributeCacheInfo *distribute_cache_info,const RectangleInfo *region,
1122  const MagickSizeType length,unsigned char *pixels)
1123{
1124  MagickBooleanType
1125    status;
1126
1127  register unsigned char
1128    *p;
1129
1130  ssize_t
1131    count;
1132
1133  unsigned char
1134    buffer[MaxTextExtent];
1135
1136  assert(distribute_cache_info != (DistributeCacheInfo *) NULL);
1137  assert(distribute_cache_info->signature == MagickSignature);
1138  assert(region != (RectangleInfo *) NULL);
1139  assert(pixels != (unsigned char *) NULL);
1140  assert(length == ((size_t) length));
1141  p=buffer;
1142  *p++='r';  /* read */
1143  (void) memcpy(p,&distribute_cache_info->session_key,
1144    sizeof(distribute_cache_info->session_key));
1145  p+=sizeof(distribute_cache_info->session_key);
1146  (void) memcpy(p,&region->width,sizeof(region->width));
1147  p+=sizeof(region->width);
1148  (void) memcpy(p,&region->height,sizeof(region->height));
1149  p+=sizeof(region->height);
1150  (void) memcpy(p,&region->x,sizeof(region->x));
1151  p+=sizeof(region->x);
1152  (void) memcpy(p,&region->y,sizeof(region->y));
1153  p+=sizeof(region->y);
1154  (void) memcpy(p,&length,sizeof(length));
1155  p+=sizeof(length);
1156  count=write(distribute_cache_info->file,buffer,p-buffer);
1157  if (count != (ssize_t) (p-buffer))
1158    return(MagickFalse);
1159  count=read(distribute_cache_info->file,(unsigned char *) pixels,(size_t)
1160    length);
1161  if (count != (ssize_t) length)
1162    return(MagickFalse);
1163  count=read(distribute_cache_info->file,&status,sizeof(status));
1164  if (count != (ssize_t) sizeof(status))
1165    return(MagickFalse);
1166  return(status != 0 ? MagickTrue : MagickFalse);
1167}
1168
1169/*
1170%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1171%                                                                             %
1172%                                                                             %
1173%                                                                             %
1174+   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               %
1175%                                                                             %
1176%                                                                             %
1177%                                                                             %
1178%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1179%
1180%  RelinquishDistributePixelCache() frees resources acquired with
1181%  OpenDistributePixelCache().
1182%
1183%  The format of the RelinquishDistributePixelCache method is:
1184%
1185%      MagickBooleanType RelinquishDistributePixelCache(
1186%        DistributeCacheInfo *distribute_cache_info)
1187%
1188%  A description of each parameter follows:
1189%
1190%    o distribute_cache_info: the distributed cache info.
1191%
1192*/
1193MagickPrivate MagickBooleanType RelinquishDistributePixelCache(
1194  DistributeCacheInfo *distribute_cache_info)
1195{
1196  register unsigned char
1197    *p;
1198
1199  ssize_t
1200    count;
1201
1202  unsigned char
1203    buffer[MaxTextExtent];
1204
1205  assert(distribute_cache_info != (DistributeCacheInfo *) NULL);
1206  assert(distribute_cache_info->signature == MagickSignature);
1207  p=buffer;
1208  *p++='d';  /* delete */
1209  (void) memcpy(p,&distribute_cache_info->session_key,
1210    sizeof(distribute_cache_info->session_key));
1211  p+=sizeof(distribute_cache_info->session_key);
1212  count=write(distribute_cache_info->file,buffer,p-buffer);
1213  if (count != (ssize_t) (p-buffer))
1214    return(MagickFalse);
1215  return(MagickTrue);
1216}
1217
1218/*
1219%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1220%                                                                             %
1221%                                                                             %
1222%                                                                             %
1223+   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   %
1224%                                                                             %
1225%                                                                             %
1226%                                                                             %
1227%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1228%
1229%  WriteDistributePixelCacheMetacontents() writes image metacontent to the
1230%  specified region of the distributed pixel cache.
1231%
1232%
1233%  The format of the WriteDistributePixelCacheMetacontents method is:
1234%
1235%      MagickBooleanType *WriteDistributePixelCacheMetacontents(
1236%        DistributeCacheInfo *distribute_cache_info,const RectangleInfo *region,
1237%        const MagickSizeType length,const unsigned char *metacontent)
1238%
1239%  A description of each parameter follows:
1240%
1241%    o distribute_cache_info: the distributed cache info.
1242%
1243%    o image: the image.
1244%
1245%    o region: write the metacontent to this region of the image.
1246%
1247%    o length: the length in bytes of the metacontent.
1248%
1249%    o metacontent: write these metacontent to the pixel cache.
1250%
1251*/
1252MagickPrivate MagickBooleanType WriteDistributePixelCacheMetacontent(
1253  DistributeCacheInfo *distribute_cache_info,const RectangleInfo *region,
1254  const MagickSizeType length,const unsigned char *metacontent)
1255{
1256  MagickBooleanType
1257    status;
1258
1259  register unsigned char
1260    *p;
1261
1262  ssize_t
1263    count;
1264
1265  unsigned char
1266    buffer[MaxTextExtent];
1267
1268  assert(distribute_cache_info != (DistributeCacheInfo *) NULL);
1269  assert(distribute_cache_info->signature == MagickSignature);
1270  assert(region != (RectangleInfo *) NULL);
1271  assert(metacontent != (unsigned char *) NULL);
1272  assert(length == ((size_t) length));
1273  p=buffer;
1274  *p++='W';  /* write */
1275  (void) memcpy(p,&distribute_cache_info->session_key,
1276    sizeof(distribute_cache_info->session_key));
1277  p+=sizeof(distribute_cache_info->session_key);
1278  (void) memcpy(p,&region->width,sizeof(region->width));
1279  p+=sizeof(region->width);
1280  (void) memcpy(p,&region->height,sizeof(region->height));
1281  p+=sizeof(region->height);
1282  (void) memcpy(p,&region->x,sizeof(region->x));
1283  p+=sizeof(region->x);
1284  (void) memcpy(p,&region->y,sizeof(region->y));
1285  p+=sizeof(region->y);
1286  (void) memcpy(p,&length,sizeof(length));
1287  p+=sizeof(length);
1288  count=write(distribute_cache_info->file,buffer,p-buffer);
1289  if (count != (ssize_t) (p-buffer))
1290    return(MagickFalse);
1291  count=write(distribute_cache_info->file,(unsigned char *) metacontent,(size_t)
1292    length);
1293  if (count != (ssize_t) length)
1294    return(MagickFalse);
1295  count=read(distribute_cache_info->file,&status,sizeof(status));
1296  if (count != (ssize_t) sizeof(status))
1297    return(MagickFalse);
1298  return(status != 0 ? MagickTrue : MagickFalse);
1299}
1300
1301/*
1302%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1303%                                                                             %
1304%                                                                             %
1305%                                                                             %
1306+   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             %
1307%                                                                             %
1308%                                                                             %
1309%                                                                             %
1310%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1311%
1312%  WriteDistributePixelCachePixels() writes image pixels to the specified
1313%  region of the distributed pixel cache.
1314%
1315%
1316%  The format of the WriteDistributePixelCachePixels method is:
1317%
1318%      MagickBooleanType *WriteDistributePixelCachePixels(
1319%        DistributeCacheInfo *distribute_cache_info,const RectangleInfo *region,
1320%        const MagickSizeType length,const unsigned char *pixels)
1321%
1322%  A description of each parameter follows:
1323%
1324%    o distribute_cache_info: the distributed cache info.
1325%
1326%    o image: the image.
1327%
1328%    o region: write the pixels to this region of the image.
1329%
1330%    o length: the length in bytes of the pixels.
1331%
1332%    o pixels: write these pixels to the pixel cache.
1333%
1334*/
1335MagickPrivate MagickBooleanType WriteDistributePixelCachePixels(
1336  DistributeCacheInfo *distribute_cache_info,const RectangleInfo *region,
1337  const MagickSizeType length,const unsigned char *pixels)
1338{
1339  MagickBooleanType
1340    status;
1341
1342  register unsigned char
1343    *p;
1344
1345  ssize_t
1346    count;
1347
1348  unsigned char
1349    buffer[MaxTextExtent];
1350
1351  assert(distribute_cache_info != (DistributeCacheInfo *) NULL);
1352  assert(distribute_cache_info->signature == MagickSignature);
1353  assert(region != (RectangleInfo *) NULL);
1354  assert(pixels != (const unsigned char *) NULL);
1355  assert(length == ((size_t) length));
1356  p=buffer;
1357  *p++='w';  /* write */
1358  (void) memcpy(p,&distribute_cache_info->session_key,
1359    sizeof(distribute_cache_info->session_key));
1360  p+=sizeof(distribute_cache_info->session_key);
1361  (void) memcpy(p,&region->width,sizeof(region->width));
1362  p+=sizeof(region->width);
1363  (void) memcpy(p,&region->height,sizeof(region->height));
1364  p+=sizeof(region->height);
1365  (void) memcpy(p,&region->x,sizeof(region->x));
1366  p+=sizeof(region->x);
1367  (void) memcpy(p,&region->y,sizeof(region->y));
1368  p+=sizeof(region->y);
1369  (void) memcpy(p,&length,sizeof(length));
1370  p+=sizeof(length);
1371  count=write(distribute_cache_info->file,buffer,p-buffer);
1372  if (count != (ssize_t) (p-buffer))
1373    return(MagickFalse);
1374  count=write(distribute_cache_info->file,(unsigned char *) pixels,(size_t)
1375    length);
1376  if (count != (ssize_t) length)
1377    return(MagickFalse);
1378  count=read(distribute_cache_info->file,&status,sizeof(status));
1379  if (count != (ssize_t) sizeof(status))
1380    return(MagickFalse);
1381  return(status != 0 ? MagickTrue : MagickFalse);
1382}
1383