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