distribute-cache.c revision e34745cf13dba9b8169e03801a7573edc5655d81
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  ssize_t
395    count;
396
397  exception=AcquireExceptionInfo();
398  image=AcquireImage((ImageInfo *) NULL,exception);
399  exception=DestroyExceptionInfo(exception);
400  count=read(file,&image->columns,sizeof(image->columns));
401  if (count != (ssize_t) sizeof(image->columns))
402    return(MagickFalse);
403  count=read(file,&image->rows,sizeof(image->rows));
404  if (count != (ssize_t) sizeof(image->rows))
405    return(MagickFalse);
406  count=read(file,&image->number_channels,sizeof(image->number_channels));
407  if (count != (ssize_t) sizeof(image->number_channels))
408    return(MagickFalse);
409  status=AddValueToSplayTree(image_registry,(const void *) session_key,image);
410  return(status);
411}
412
413static MagickBooleanType DestroyDistributeCache(SplayTreeInfo *image_registry,
414  int file,const MagickSizeType session_key)
415{
416  char
417    key[MaxTextExtent];
418
419  (void) FormatLocaleString(key,MaxTextExtent,"%.20g",(double) session_key);
420  return(DeleteImageRegistry(key));
421}
422
423static MagickBooleanType ReadDistributeCache(SplayTreeInfo *image_registry,
424  int file,const MagickSizeType session_key)
425{
426  ExceptionInfo
427    *exception;
428
429  Image
430    *image;
431
432  RectangleInfo
433    region;
434
435  register const Quantum
436    *p;
437
438  size_t
439    length;
440
441  ssize_t
442    count;
443
444  image=(Image *) GetValueFromSplayTree(image_registry,(const void *)
445    session_key);
446  if (image == (Image *) NULL)
447    return(MagickFalse);
448  count=read(file,&region.width,sizeof(region.width));
449  if (count != (ssize_t) sizeof(region.width))
450    return(MagickFalse);
451  count=read(file,&region.height,sizeof(region.height));
452  if (count != (ssize_t) sizeof(region.height))
453    return(MagickFalse);
454  count=read(file,&region.x,sizeof(region.x));
455  if (count != (ssize_t) sizeof(region.x))
456    return(MagickFalse);
457  count=read(file,&region.y,sizeof(region.y));
458  if (count != (ssize_t) sizeof(region.y))
459    return(MagickFalse);
460  count=read(file,&length,sizeof(length));
461  if (count != (ssize_t) sizeof(length))
462    return(MagickFalse);
463  exception=AcquireExceptionInfo();
464  p=GetVirtualPixels(image,region.x,region.y,region.width,region.height,
465    exception);
466  exception=DestroyExceptionInfo(exception);
467  if (p == (const Quantum *) NULL)
468    return(MagickFalse);
469  count=write(file,p,length);
470  if (count != (ssize_t) length)
471    return(MagickFalse);
472  return(MagickTrue);
473}
474
475static MagickBooleanType WriteDistributeCache(SplayTreeInfo *image_registry,
476  int file,const MagickSizeType session_key)
477{
478  ExceptionInfo
479    *exception;
480
481  Image
482    *image;
483
484  MagickBooleanType
485    status;
486
487  RectangleInfo
488    region;
489
490  register Quantum
491    *p;
492
493  size_t
494    length;
495
496  ssize_t
497    count;
498
499  image=(Image *) GetValueFromSplayTree(image_registry,(const void *)
500    session_key);
501  if (image == (Image *) NULL)
502    return(MagickFalse);
503  count=read(file,&region.width,sizeof(region.width));
504  if (count != (ssize_t) sizeof(region.width))
505    return(MagickFalse);
506  count=read(file,&region.height,sizeof(region.height));
507  if (count != (ssize_t) sizeof(region.height))
508    return(MagickFalse);
509  count=read(file,&region.x,sizeof(region.x));
510  if (count != (ssize_t) sizeof(region.x))
511    return(MagickFalse);
512  count=read(file,&region.y,sizeof(region.y));
513  if (count != (ssize_t) sizeof(region.y))
514    return(MagickFalse);
515  count=read(file,&length,sizeof(length));
516  if (count != (ssize_t) sizeof(length))
517    return(MagickFalse);
518  exception=AcquireExceptionInfo();
519  p=GetAuthenticPixels(image,region.x,region.y,region.width,region.height,
520    exception);
521  exception=DestroyExceptionInfo(exception);
522  if (p == (Quantum *) NULL)
523    return(MagickFalse);
524  count=read(file,p,length);
525  if (count != (ssize_t) length)
526    return(MagickFalse);
527  status=SyncAuthenticPixels(image,exception);
528  return(status);
529}
530
531static void *DistributePixelCacheClient(void *socket)
532{
533  const char
534    *shared_secret;
535
536  int
537    client_socket;
538
539  MagickBooleanType
540    status;
541
542  MagickSizeType
543    key,
544    session_key;
545
546  RandomInfo
547    *random_info;
548
549  SplayTreeInfo
550    *image_registry;
551
552  ssize_t
553    count;
554
555  StringInfo
556    *secret;
557
558  unsigned char
559    command,
560    session[MaxTextExtent];
561
562  /*
563    Generate session key.
564  */
565  shared_secret=GetPolicyValue("shared-secret");
566  if (shared_secret == (const char *) NULL)
567    ThrowFatalException(CacheFatalError,"shared secret expected");
568  (void) CopyMagickString((char *) session,shared_secret,MaxTextExtent-
569    DPCSessionKeyLength);
570  random_info=AcquireRandomInfo();
571  secret=GetRandomKey(random_info,DPCSessionKeyLength);
572  (void) memcpy(session+strlen(shared_secret),GetStringInfoDatum(secret),
573    DPCSessionKeyLength);
574  session_key=CRC64(session,strlen(shared_secret)+DPCSessionKeyLength);
575  random_info=DestroyRandomInfo(random_info);
576  image_registry=NewSplayTree((int (*)(const void *,const void *)) NULL,
577    (void *(*)(void *)) NULL,(void *(*)(void *)) NULL);
578  client_socket=(*(int *) socket);
579  count=write(client_socket,GetStringInfoDatum(secret),DPCSessionKeyLength);
580  secret=DestroyStringInfo(secret);
581  for ( ; ; )
582  {
583    count=read(client_socket,&command,1);
584    if (count <= 0)
585      break;
586    count=read(client_socket,&key,sizeof(key));
587    if ((count != (ssize_t) sizeof(key)) && (key != session_key))
588      break;
589    status=MagickFalse;
590    switch (command)
591    {
592      case 'c':
593      {
594        status=CreateDistributeCache(image_registry,client_socket,session_key);
595        break;
596      }
597      case 'r':
598      {
599        status=ReadDistributeCache(image_registry,client_socket,session_key);
600        break;
601      }
602      case 'u':
603      {
604        status=WriteDistributeCache(image_registry,client_socket,session_key);
605        break;
606      }
607      case 'd':
608      {
609        status=DestroyDistributeCache(image_registry,client_socket,session_key);
610        break;
611      }
612      default:
613        break;
614    }
615    count=write(client_socket,&status,sizeof(status));
616    if (count != (ssize_t) sizeof(status))
617      break;
618  }
619  return((void *) NULL);
620}
621
622MagickExport void DistributePixelCacheServer(const size_t port,
623  ExceptionInfo *exception)
624{
625#if defined(MAGICKCORE_HAVE_SOCKET) && defined(MAGICKCORE_THREAD_SUPPORT)
626  int
627    server_socket,
628    status;
629
630  pthread_attr_t
631    attributes;
632
633  pthread_t
634    threads;
635
636  struct sockaddr_in
637    address;
638
639  /*
640    Launch distributed pixel cache server.
641  */
642  server_socket=socket(AF_INET,SOCK_STREAM,0);
643  address.sin_family=AF_INET;
644  address.sin_port=htons(port);
645  address.sin_addr.s_addr=htonl(INADDR_ANY);
646  status=bind(server_socket,(struct sockaddr *) &address,(socklen_t)
647    sizeof(address));
648  if (status != 0)
649    ThrowFatalException(CacheFatalError,"UnableToBind");
650  status=listen(server_socket,32);
651  if (status != 0)
652    ThrowFatalException(CacheFatalError,"UnableToListen");
653  pthread_attr_init(&attributes);
654  for ( ; ; )
655  {
656    int
657      client_socket;
658
659    socklen_t
660      length;
661
662    length=(socklen_t) sizeof(address);
663    client_socket=accept(server_socket,(struct sockaddr *) &address,&length);
664    if (client_socket == -1)
665      ThrowFatalException(CacheFatalError,"UnableToEstablishConnection");
666    status=pthread_create(&threads,&attributes,DistributePixelCacheClient,
667      (void *) &client_socket);
668    if (status == -1)
669      ThrowFatalException(CacheFatalError,"UnableToCreateClientThread");
670  }
671  (void) close(server_socket);
672#else
673  ThrowFatalException(MissingDelegateError,"distributed pixel cache");
674#endif
675}
676
677/*
678%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
679%                                                                             %
680%                                                                             %
681%                                                                             %
682%   O p e n D i s t r i b u t e P i x e l C a c h e                           %
683%                                                                             %
684%                                                                             %
685%                                                                             %
686%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
687%
688%  OpenDistributePixelCache() opens a pixel cache on a remote server.
689%
690%  The format of the OpenDistributePixelCache method is:
691%
692%      MagickBooleanType *OpenDistributePixelCache(
693%        DistributeCacheInfo *distribute_cache_info,Image *image)
694%
695%  A description of each parameter follows:
696%
697%    o distribute_cache_info: the distributed cache info.
698%
699%    o image: the image.
700%
701*/
702MagickPrivate MagickBooleanType OpenDistributePixelCache(
703  DistributeCacheInfo *distribute_cache_info,Image *image)
704{
705  int
706    file;
707
708  MagickBooleanType
709    status;
710
711  MagickSizeType
712    session_key;
713
714  ssize_t
715    count;
716
717  assert(distribute_cache_info != (DistributeCacheInfo *) NULL);
718  assert(distribute_cache_info->signature == MagickSignature);
719  assert(image != (Image *) NULL);
720  assert(image->signature == MagickSignature);
721  file=distribute_cache_info->file;
722  session_key=distribute_cache_info->session_key;
723  count=write(file,"c",1);
724  if (count != 1)
725    return(MagickFalse);
726  count=write(file,&session_key,sizeof(session_key));
727  if (count != (ssize_t) sizeof(session_key))
728    return(MagickFalse);
729  count=write(file,&image->columns,sizeof(image->columns));
730  if (count != (ssize_t) sizeof(image->columns))
731    return(MagickFalse);
732  count=write(file,&image->rows,sizeof(image->rows));
733  if (count != (ssize_t) sizeof(image->rows))
734    return(MagickFalse);
735  count=write(file,&image->number_channels,sizeof(image->number_channels));
736  if (count != (ssize_t) sizeof(image->number_channels))
737    return(MagickFalse);
738  count=read(file,&status,sizeof(status));
739  if (count != (ssize_t) sizeof(status))
740    return(MagickFalse);
741  return(MagickTrue);
742}
743
744/*
745%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
746%                                                                             %
747%                                                                             %
748%                                                                             %
749%   R e a d D i s t r i b u t e P i x e l C a c h e                           %
750%                                                                             %
751%                                                                             %
752%                                                                             %
753%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
754%
755%  ReadDistributePixelCache() reads pixels from the specified region of the
756%  distributed pixel cache.
757%
758%  The format of the ReadDistributePixelCache method is:
759%
760%      MagickBooleanType *ReadDistributePixelCache(
761%        DistributeCacheInfo *distribute_cache_info,const RectangleInfo *region,
762%        const MagickSizeType length,Quantum *pixels)
763%
764%  A description of each parameter follows:
765%
766%    o distribute_cache_info: the distributed cache info.
767%
768%    o image: the image.
769%
770%    o region: read the pixels from this region of the image.
771%
772%    o length: write the pixels to this region of the image.
773%
774%    o pixels: read these pixels from the pixel cache.
775%
776*/
777MagickPrivate MagickBooleanType ReadDistributePixelCache(
778  DistributeCacheInfo *distribute_cache_info,const RectangleInfo *region,
779  const MagickSizeType length,Quantum *pixels)
780{
781  int
782    file;
783
784  MagickBooleanType
785    status;
786
787  MagickSizeType
788    session_key;
789
790  ssize_t
791    count;
792
793  assert(distribute_cache_info != (DistributeCacheInfo *) NULL);
794  assert(distribute_cache_info->signature == MagickSignature);
795  assert(region != (RectangleInfo *) NULL);
796  assert(pixels != (Quantum *) NULL);
797  file=distribute_cache_info->file;
798  session_key=distribute_cache_info->session_key;
799  count=write(file,"r",1);
800  if (count != 1)
801    return(MagickFalse);
802  count=write(file,&session_key,sizeof(session_key));
803  if (count != (ssize_t) sizeof(session_key))
804    return(MagickFalse);
805  count=write(file,&region->width,sizeof(region->width));
806  if (count != (ssize_t) sizeof(region->width))
807    return(MagickFalse);
808  count=write(file,&region->height,sizeof(region->height));
809  if (count != (ssize_t) sizeof(region->height))
810    return(MagickFalse);
811  count=write(file,&region->x,sizeof(region->x));
812  if (count != (ssize_t) sizeof(region->x))
813    return(MagickFalse);
814  count=write(file,&region->y,sizeof(region->y));
815  if (count != (ssize_t) sizeof(region->y))
816    return(MagickFalse);
817  if (length != ((size_t) length))
818    return(MagickFalse);
819  count=write(file,&length,sizeof(length));
820  if (count != (ssize_t) sizeof(length))
821    return(MagickFalse);
822  count=read(file,(unsigned char *) pixels,(size_t) length);
823  if (count != (ssize_t) length)
824    return(MagickFalse);
825  count=read(file,&status,sizeof(status));
826  if (count != (ssize_t) sizeof(status))
827    return(MagickFalse);
828  return(status != 0 ? MagickTrue : MagickFalse);
829}
830
831/*
832%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
833%                                                                             %
834%                                                                             %
835%                                                                             %
836%   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               %
837%                                                                             %
838%                                                                             %
839%                                                                             %
840%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
841%
842%  RelinquishDistributePixelCache() frees resources acquired with
843%  OpenDistributePixelCache().
844%
845%  The format of the RelinquishDistributePixelCache method is:
846%
847%      void RelinquishDistributePixelCache(
848%        DistributeCacheInfo *distribute_cache_info)
849%
850%  A description of each parameter follows:
851%
852%    o distribute_cache_info: the distributed cache info.
853%
854*/
855MagickPrivate void RelinquishDistributePixelCache(
856  DistributeCacheInfo *distribute_cache_info)
857{
858  int
859    file;
860
861  MagickSizeType
862    session_key;
863
864  assert(distribute_cache_info != (DistributeCacheInfo *) NULL);
865  assert(distribute_cache_info->signature == MagickSignature);
866  file=distribute_cache_info->file;
867  session_key=distribute_cache_info->session_key;
868  (void) write(file,"c",1);
869  (void) write(file,&session_key,sizeof(session_key));
870}
871
872/*
873%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
874%                                                                             %
875%                                                                             %
876%                                                                             %
877%   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                         %
878%                                                                             %
879%                                                                             %
880%                                                                             %
881%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
882%
883%  WriteDistributePixelCache() writes image pixels to the specified region of
884%  the distributed pixel cache.
885%
886%
887%  The format of the WriteDistributePixelCache method is:
888%
889%      MagickBooleanType *WriteDistributePixelCache(
890%        DistributeCacheInfo *distribute_cache_info,const RectangleInfo *region,
891%        const MagickSizeType length,const Quantum *pixels)
892%
893%  A description of each parameter follows:
894%
895%    o distribute_cache_info: the distributed cache info.
896%
897%    o image: the image.
898%
899%    o region: write the pixels to this region of the image.
900%
901%    o length: write the pixels to this region of the image.
902%
903%    o pixels: write these pixels to the pixel cache.
904%
905*/
906MagickPrivate MagickBooleanType WriteDistributePixelCache(
907  DistributeCacheInfo *distribute_cache_info,const RectangleInfo *region,
908  const MagickSizeType length,const Quantum *pixels)
909{
910  int
911    file;
912
913  MagickBooleanType
914    status;
915
916  MagickSizeType
917    session_key;
918
919  ssize_t
920    count;
921
922  assert(distribute_cache_info != (DistributeCacheInfo *) NULL);
923  assert(distribute_cache_info->signature == MagickSignature);
924  assert(region != (RectangleInfo *) NULL);
925  assert(pixels != (Quantum *) NULL);
926  file=distribute_cache_info->file;
927  session_key=distribute_cache_info->session_key;
928  count=write(file,"u",1);
929  if (count != 1)
930    return(MagickFalse);
931  count=write(file,&session_key,sizeof(session_key));
932  if (count != (ssize_t) sizeof(session_key))
933    return(MagickFalse);
934  count=write(file,&region->width,sizeof(region->width));
935  if (count != (ssize_t) sizeof(region->width))
936    return(MagickFalse);
937  count=write(file,&region->height,sizeof(region->height));
938  if (count != (ssize_t) sizeof(region->height))
939    return(MagickFalse);
940  count=write(file,&region->x,sizeof(region->x));
941  if (count != (ssize_t) sizeof(region->x))
942    return(MagickFalse);
943  count=write(file,&region->y,sizeof(region->y));
944  if (count != (ssize_t) sizeof(region->y))
945    return(MagickFalse);
946  if (length != ((size_t) length))
947    return(MagickFalse);
948  count=write(file,&length,sizeof(length));
949  if (count != (ssize_t) sizeof(length))
950    return(MagickFalse);
951  count=write(file,(unsigned char *) pixels,(size_t) length);
952  if (count != (ssize_t) length)
953    return(MagickFalse);
954  count=read(file,&status,sizeof(status));
955  if (count != (ssize_t) sizeof(status))
956    return(MagickFalse);
957  return(status != 0 ? MagickTrue : MagickFalse);
958}
959